﻿namespace Vanara.PInvoke;

/// <summary>Methods and data types found in Crypt32.dll.</summary>
public static partial class Crypt32
{
	/// <summary>A callback function used to read from and write data to a disk when processing large messages.</summary>
	/// <param name="pvArg">The arguments specified by CMSG_STREAM_INFO.</param>
	/// <param name="pbData">A pointer to a block of processed data that is available to the application.</param>
	/// <param name="cbData">The size, in bytes, of the block of processed data at pbData.</param>
	/// <param name="fFinal">
	/// Specifies that the last block of data is being processed and that this is the last time the callback will be executed.
	/// </param>
	/// <returns><see langword="true"/> on success; <see langword="false"/> on failure.</returns>
	[PInvokeData("wincrypt.h", MSDNShortId = "a4e7f6e8-351f-4981-b223-50b65f503394")]
	public delegate bool PFN_CMSG_STREAM_OUTPUT([In] IntPtr pvArg, [In] IntPtr pbData, uint cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal);

	/// <summary>
	/// The <c>CryptGetSignerCertificateCallback</c> user supplied callback function is used with the CRYPT_VERIFY_MESSAGE_PARA
	/// structure to get and verify a message signer's certificate.
	/// </summary>
	/// <param name="pvGetArg"/>
	/// <param name="dwCertEncodingType">
	/// <para>
	/// Specifies the type of encoding used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pSignerId">
	/// A pointer to a CERT_INFO structure containing the issuer and serial number. Can be <c>NULL</c> if there is no content or signer.
	/// </param>
	/// <param name="hMsgCertStore">A handle to the certificate store containing all the certificates and CRLs in the signed message.</param>
	/// <returns>
	/// If a signer certificate is found, the function returns a pointer to a read-only CERT_CONTEXT. The returned <c>CERT_CONTEXT</c>
	/// was obtained either from a certificate store or was created using CertCreateCertificateContext. In either case, it must be freed
	/// using CertFreeCertificateContext. If this function fails, the return value is <c>NULL</c>.
	/// </returns>
	/// <remarks>If the message does not contain content or signers, the function is called with pSignerId set to <c>NULL</c>.</remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nc-wincrypt-pfn_crypt_get_signer_certificate
	// PFN_CRYPT_GET_SIGNER_CERTIFICATE PfnCryptGetSignerCertificate; PCCERT_CONTEXT PfnCryptGetSignerCertificate( void *pvGetArg, DWORD
	// dwCertEncodingType, PCERT_INFO pSignerId, HCERTSTORE hMsgCertStore ) {...}
	[PInvokeData("wincrypt.h", MSDNShortId = "557ebb26-cce0-4c41-b49c-769b2831cf35")]
	public delegate PCCERT_CONTEXT PFN_CRYPT_GET_SIGNER_CERTIFICATE([In, Out] IntPtr pvGetArg, CertEncodingType dwCertEncodingType, in CERT_INFO pSignerId, HCERTSTORE hMsgCertStore);

	/// <summary>Flags for various structure behaviors.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "1601d860-6054-4650-a033-ea088655b7e4")]
	[Flags]
	public enum CryptMsgActionFlags
	{
		/// <summary>
		/// If the encoded output is to be a CMSG_SIGNED inner content of an outer cryptographic message such as a CMSG_ENVELOPED
		/// message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set.
		/// </summary>
		CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG = 0x00000001,

		/// <summary>CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content into an OCTET STRING.</summary>
		CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG = 0x00000002,

		/// <summary>
		/// CRYPT_MESSAGE_KEYID_SIGNER_FLAG can be set to identify signers by their Key Identifier and not their Issuer and Serial Number.
		/// </summary>
		CRYPT_MESSAGE_KEYID_SIGNER_FLAG = 0x00000004,

		/// <summary>
		/// CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT flag,
		/// see CryptAcquireContext.
		/// </summary>
		CRYPT_MESSAGE_SILENT_KEYSET_FLAG = 0x00000040,
	}

	/// <summary>Message control types.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "a990d44d-2993-429f-b817-2a834105ecef")]
	public enum CryptMsgControlType
	{
		/// <summary>A BLOB that contains the encoded bytes of attribute certificate.</summary>
		[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
		CMSG_CTRL_ADD_ATTR_CERT = 14,

		/// <summary>A CRYPT_INTEGER_BLOB structure that contains the encoded bytes of the certificate to be added to the message.</summary>
		[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
		CMSG_CTRL_ADD_CERT = 10,

		/// <summary>
		/// A CMSG_CMS_SIGNER_INFO structure that contains signer information. This operation differs from CMSG_CTRL_ADD_SIGNER because
		/// the signer information contains the signature.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CMS_SIGNER_INFO))]
		CMSG_CTRL_ADD_CMS_SIGNER_INFO = 20,

		/// <summary>A BLOB that contains the encoded bytes of the CRL to be added to the message.</summary>
		[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
		CMSG_CTRL_ADD_CRL = 12,

		/// <summary>A CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</summary>
		[CorrespondingType(typeof(CMSG_SIGNER_ENCODE_INFO))]
		CMSG_CTRL_ADD_SIGNER = 6,

		/// <summary>
		/// A CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA structure that contains the index of the signer and a BLOB that contains the
		/// unauthenticated attribute information to be added to the message.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA))]
		CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR = 8,

		/// <summary>
		/// A CMSG_CTRL_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. This value is
		/// applicable to RSA recipients. This operation specifies that the CryptMsgControl function search the recipient index to
		/// obtain the key transport recipient information. If the function fails, GetLastError will return CRYPT_E_INVALID_INDEX if no
		/// key transport recipient is found.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_DECRYPT_PARA))]
		CMSG_CTRL_DECRYPT = 2,

		/// <summary>The index of the attribute certificate to be removed.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CTRL_DEL_ATTR_CERT = 15,

		/// <summary>The index of the certificate to be deleted from the message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CTRL_DEL_CERT = 11,

		/// <summary>The index of the CRL to be deleted from the message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CTRL_DEL_CRL = 13,

		/// <summary>The index of the signer to be deleted.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CTRL_DEL_SIGNER = 7,

		/// <summary>
		/// A CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA structure that contains an index that specifies the signer and the index that
		/// specifies the signer's unauthenticated attribute to be deleted.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA))]
		CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR = 9,

		/// <summary>
		/// A CERT_STRONG_SIGN_PARA structure used to perform strong signature checking.
		/// <para>
		/// To check for a strong signature, specify this control type before calling CryptMsgGetAndVerifySigner or before calling
		/// CryptMsgControl with the following control types set:
		/// </para>
		/// <para>CMSG_CTRL_VERIFY_SIGNATURE</para>
		/// <para>CMSG_CTRL_VERIFY_SIGNATURE_EX</para>
		/// <para>
		/// After the signature is successfully verified, this function checks for a strong signature. If the signature is not strong,
		/// the operation will fail and the GetLastError value will be set to NTE_BAD_ALGID.
		/// </para>
		/// </summary>
		[CorrespondingType(typeof(CERT_STRONG_SIGN_PARA))]
		CMSG_CTRL_ENABLE_STRONG_SIGNATURE = 21,

		/// <summary>
		/// A CMSG_CTRL_KEY_AGREE_DECRYPT_PARA structure used to decrypt the message for the specified key agreement session key. Key
		/// agreement is used with Diffie-Hellman encryption/decryption.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_KEY_AGREE_DECRYPT_PARA))]
		CMSG_CTRL_KEY_AGREE_DECRYPT = 17,

		/// <summary>
		/// A CMSG_CTRL_KEY_TRANS_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. Key
		/// transport is used with RSA encryption/decryption.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_KEY_TRANS_DECRYPT_PARA))]
		CMSG_CTRL_KEY_TRANS_DECRYPT = 16,

		/// <summary>
		/// A CMSG_CTRL_MAIL_LIST_DECRYPT_PARA structure used to decrypt the message for the specified recipient using a previously
		/// distributed key-encryption key (KEK).
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_MAIL_LIST_DECRYPT_PARA))]
		CMSG_CTRL_MAIL_LIST_DECRYPT = 18,

		/// <summary>This value is not used.</summary>
		CMSG_CTRL_VERIFY_HASH = 5,

		/// <summary>A CERT_INFO structure that identifies the signer of the message whose signature is to be verified.</summary>
		[CorrespondingType(typeof(CERT_INFO))]
		CMSG_CTRL_VERIFY_SIGNATURE = 1,

		/// <summary>
		/// A CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA structure that specifies the signer index and public key to verify the message
		/// signature. The signer public key can be a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a certificate chain context.
		/// </summary>
		[CorrespondingType(typeof(CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA))]
		CMSG_CTRL_VERIFY_SIGNATURE_EX = 19,
	}

	/// <summary>Flags for message functions.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
	[Flags]
	public enum CryptMsgFlags
	{
		/// <summary>
		/// Indicates that streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to
		/// be streamed into an enclosing message.
		/// </summary>
		CMSG_BARE_CONTENT_FLAG = 0x00000001,

		/// <summary></summary>
		CMSG_LENGTH_ONLY_FLAG = 0x00000002,

		/// <summary>Indicates that there is detached data being supplied for the subsequent calls to CryptMsgUpdate.</summary>
		CMSG_DETACHED_FLAG = 0x00000004,

		/// <summary>
		/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
		/// otherwise be required.
		/// </summary>
		CMSG_AUTHENTICATED_ATTRIBUTES_FLAG = 0x00000008,

		/// <summary>
		/// Used to calculate the size of a DER encoding of a message to be nested inside an enveloped message. This is particularly
		/// useful when streaming is being performed.
		/// </summary>
		CMSG_CONTENTS_OCTETS_FLAG = 0x00000010,

		/// <summary></summary>
		CMSG_MAX_LENGTH_FLAG = 0x00000020,

		/// <summary>
		/// Non-Data type inner content is encapsulated within an OCTET STRING. This flag is applicable for both Signed and Enveloped messages.
		/// </summary>
		CMSG_CMS_ENCAPSULATED_CONTENT_FLAG = 0x00000040,

		/// <summary></summary>
		CMSG_SIGNED_DATA_NO_SIGN_FLAG = 0x00000080,

		/// <summary>
		/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released
		/// if the function fails.
		/// </summary>
		CMSG_CRYPT_RELEASE_CONTEXT_FLAG = 0x00008000,
	}

	/// <summary>Flags used by CMSG_KEY_AGREE_RECIPIENT_INFO.</summary>
	[PInvokeData("wincrypt.h")]
	public enum CryptMsgKeyOriginator
	{
		/// <summary>OriginatorCertId</summary>
		CMSG_KEY_AGREE_ORIGINATOR_CERT = 1,

		/// <summary>OriginatorPublicKeyInfo</summary>
		CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY = 2,
	}

	/// <summary>
	/// Indicates the parameter types of data to be retrieved.
	/// <para>
	/// For an encoded message, only the CMSG_BARE_CONTENT, CMSG_ENCODE_SIGNER, CMSG_CONTENT_PARAM and CMSG_COMPUTED_HASH_PARAM
	/// dwParamTypes are valid.
	/// </para>
	/// </summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "5a05eb09-208f-4e94-abfa-c2f14c0a3164")]
	public enum CryptMsgParamType
	{
		/// <summary>
		/// Returns the message type of a decoded message of unknown type. The retrieved message type can be compared to supported types
		/// to determine whether processing can continued. For supported message types, see the dwMessageType parameter of CryptMsgOpenToDecode.
		/// </summary>
		[CorrespondingType(typeof(uint))]
		CMSG_TYPE_PARAM = 1,

		/// <summary>
		/// Returns the whole PKCS #7 message from a message opened to encode. Retrieves the inner content of a message opened to
		/// decode. If the message is enveloped, the inner type is data, and CryptMsgControl has been called to decrypt the message, the
		/// decrypted content is returned. If the inner type is not data, the encoded BLOB that requires further decoding is returned.
		/// If the message is not enveloped and the inner content is DATA, the returned data is the octets of the inner content. This
		/// type is applicable to both encode and decode.
		/// <para>For decoding, if the type is CMSG_DATA, the content's octets are returned; else, the encoded inner content is returned.</para>
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
		CMSG_CONTENT_PARAM = 2,

		/// <summary>
		/// Retrieves the encoded content of an encoded cryptographic message, without the outer layer of the CONTENT_INFO structure.
		/// That is, only the encoding of the PKCS #7 defined ContentInfo.content field is returned.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_BARE_CONTENT_PARAM = 3,

		/// <summary>Returns the inner content type of a received message. This type is not applicable to messages of type DATA.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_INNER_CONTENT_TYPE_PARAM = 4,

		/// <summary>Returns the number of signers of a received SIGNED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_SIGNER_COUNT_PARAM = 5,

		/// <summary>
		/// Returns information on a message signer. This includes the issuer and serial number of the signer's certificate and
		/// authenticated and unauthenticated attributes of the signer's certificate. To retrieve signer information on all of the
		/// signers of a message, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_INFO_PARAM = 6,

		/// <summary>
		/// Returns information on a message signer needed to identify the signer's certificate. A certificate's Issuer and SerialNumber
		/// can be used to uniquely identify a certificate for retrieval. To retrieve information for all the signers, repetitively call
		/// CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one. Only the Issuer and SerialNumber fields in the
		/// CERT_INFO structure returned contain available, valid data.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_CERT_INFO_PARAM = 7,

		/// <summary>
		/// Returns the hash algorithm used by a signer of the message. To get the hash algorithm for a specified signer, call
		/// CryptMsgGetParam with dwIndex equal to that signer's index.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_HASH_ALGORITHM_PARAM = 8,

		/// <summary>
		/// Returns the authenticated attributes of a message signer. To retrieve the authenticated attributes for a specified signer,
		/// call CryptMsgGetParam with dwIndex equal to that signer's index.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_AUTH_ATTR_PARAM = 9,

		/// <summary>
		/// Returns a message signer's unauthenticated attributes. To retrieve the unauthenticated attributes for a specified signer,
		/// call CryptMsgGetParam with dwIndex equal to that signer's index.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_UNAUTH_ATTR_PARAM = 10,

		/// <summary>Returns the number of certificates in a received SIGNED or ENVELOPED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CERT_COUNT_PARAM = 11,

		/// <summary>
		/// Returns a signer's certificate. To get all of the signer's certificates, call CryptMsgGetParam, varying dwIndex from 0 to
		/// the number of available certificates minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_CERT_PARAM = 12,

		/// <summary>Returns the count of CRLs in a received, SIGNED or ENVELOPED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CRL_COUNT_PARAM = 13,

		/// <summary>
		/// Returns a CRL. To get all the CRLs, call CryptMsgGetParam, varying dwIndex from 0 to the number of available CRLs minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_CRL_PARAM = 14,

		/// <summary>Returns the encryption algorithm used to encrypt an ENVELOPED message.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ENVELOPE_ALGORITHM_PARAM = 15,

		/// <summary>Returns the number of key transport recipients of an ENVELOPED received message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_RECIPIENT_COUNT_PARAM = 17,

		/// <summary>
		/// Returns the index of the key transport recipient used to decrypt an ENVELOPED message. This value is available only after a
		/// message has been decrypted.
		/// </summary>
		[CorrespondingType(typeof(uint))]
		CMSG_RECIPIENT_INDEX_PARAM = 18,

		/// <summary>
		/// Returns certificate information about a key transport message's recipient. To get certificate information on all key
		/// transport message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus
		/// one. Only the Issuer, SerialNumber, and PublicKeyAlgorithm members of the CERT_INFO structure returned are available and valid.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_RECIPIENT_INFO_PARAM = 19,

		/// <summary>Returns the hash algorithm used to hash the message when it was created.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_HASH_ALGORITHM_PARAM = 20,

		/// <summary>Returns the hash value stored in the message when it was created.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_HASH_DATA_PARAM = 21,

		/// <summary>Returns the hash calculated of the data in the message. This type is applicable to both encode and decode.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_COMPUTED_HASH_PARAM = 22,

		/// <summary>Returns the encryption algorithm used to encrypted the message.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ENCRYPT_PARAM = 26,

		/// <summary>Returns the encrypted hash of a signature. Typically used for performing time-stamping.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ENCRYPTED_DIGEST = 27,

		/// <summary>Returns the encoded CMSG_SIGNER_INFO signer information for a message signer.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ENCODED_SIGNER = 28,

		/// <summary>
		/// Changes the contents of an already encoded message. The message must first be decoded with a call to CryptMsgOpenToDecode.
		/// Then the change to the message is made through a call to CryptMsgControl, CryptMsgCountersign, or
		/// CryptMsgCountersignEncoded. The message is then encoded again with a call to CryptMsgGetParam, specifying
		/// CMSG_ENCODED_MESSAGE to get a new encoding that reflects the changes made. This can be used, for instance, to add a
		/// time-stamp attribute to a message.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ENCODED_MESSAGE = 29,

		/// <summary>Returns the version of the decoded message. For more information, see the table in the Remarks section.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_VERSION_PARAM = 30,

		/// <summary>Returns the count of the attribute certificates in a SIGNED or ENVELOPED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_ATTR_CERT_COUNT_PARAM = 31,

		/// <summary>
		/// Retrieves an attribute certificate. To get all the attribute certificates, call CryptMsgGetParam varying dwIndex set to 0
		/// the number of attributes minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_ATTR_CERT_PARAM = 32,

		/// <summary>Returns the total count of all message recipients including key agreement and mail list recipients.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CMS_RECIPIENT_COUNT_PARAM = 33,

		/// <summary>Returns the index of the key transport, key agreement, or mail list recipient used to decrypt an ENVELOPED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CMS_RECIPIENT_INDEX_PARAM = 34,

		/// <summary>Returns the index of the encrypted key of a key agreement recipient used to decrypt an ENVELOPED message.</summary>
		[CorrespondingType(typeof(uint))]
		CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM = 35,

		/// <summary>
		/// Returns information about a key transport, key agreement, or mail list recipient. It is not limited to key transport message
		/// recipients. To get information on all of a message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0
		/// to the number of recipients minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_CMS_RECIPIENT_INFO_PARAM = 36,

		/// <summary>Returns the unprotected attributes in an enveloped message.</summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_UNPROTECTED_ATTR_PARAM = 37,

		/// <summary>
		/// Returns information on a message signer needed to identify the signer's public key. This could be a certificate's Issuer and
		/// SerialNumber, a KeyID, or a HashId. To retrieve information for all the signers, call CryptMsgGetParam varying dwIndex from
		/// 0 to the number of signers minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_SIGNER_CERT_ID_PARAM = 38,

		/// <summary>
		/// Returns information on a message signer. This includes a signerId and authenticated and unauthenticated attributes. To
		/// retrieve signer information on all of the signers of a message, call CryptMsgGetParam varying dwIndex from 0 to the number
		/// of signers minus one.
		/// </summary>
		[CorrespondingType(typeof(byte[]))]
		CMSG_CMS_SIGNER_INFO_PARAM = 39,
	}

	/// <summary>Message signer type.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "56b73de8-c170-46f6-b488-096475b59c15")]
	public enum CryptMsgSignerType
	{
		/// <summary>CERT_PUBLIC_KEY_INFO</summary>
		[CorrespondingType(typeof(CERT_PUBLIC_KEY_INFO))]
		CMSG_VERIFY_SIGNER_PUBKEY = 1,

		/// <summary>CERT_CONTEXT</summary>
		[CorrespondingType(typeof(CERT_CONTEXT))]
		CMSG_VERIFY_SIGNER_CERT = 2,

		/// <summary>CERT_CHAIN_CONTEXT</summary>
		[CorrespondingType(typeof(CERT_CHAIN_CONTEXT))]
		CMSG_VERIFY_SIGNER_CHAIN = 3,

		/// <summary>The CMSG verify signer null</summary>
		CMSG_VERIFY_SIGNER_NULL = 4,
	}

	/// <summary>Message types.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
	public enum CryptMsgType
	{
		/// <summary>An octet (BYTE) string.</summary>
		CMSG_DATA = 1,

		/// <summary>CMSG_SIGNED_ENCODE_INFO</summary>
		CMSG_SIGNED = 2,

		/// <summary>CMSG_ENVELOPED_ENCODE_INFO</summary>
		CMSG_ENVELOPED = 3,

		/// <summary>Not implemented.</summary>
		CMSG_SIGNED_AND_ENVELOPED = 4,

		/// <summary>CMSG_HASHED_ENCODE_INFO</summary>
		CMSG_HASHED = 5,

		/// <summary>Not implemented.</summary>
		CMSG_ENCRYPTED = 6,
	}

	/// <summary>Flags that modify the function behavior.</summary>
	[PInvokeData("wincrypt.h", MSDNShortId = "da756cd5-1dec-4d88-9c90-76dd263035eb")]
	public enum CryptMsgVerifyCounterFlags
	{
		/// <summary>
		/// Performs a strong signature check after successful signature verification. Set the pvExtra parameter to point to a
		/// CERT_STRONG_SIGN_PARA structure that contains the parameters needed to check the signature strength.. Windows 8 and Windows
		/// Server 2012: Support for this flag begins.
		/// </summary>
		CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG = 0x00000001
	}

	/// <summary>
	/// <para>The <c>CryptDecodeMessage</c> function decodes, decrypts, and verifies a cryptographic message.</para>
	/// <para>
	/// This function can be used when the type of cryptographic message is unknown. The dwMsgTypeFlags constants can be combined with a
	/// bitwise- <c>OR</c> operation so that the function will try to find one of the types. When one of the types is found, the
	/// function reports the type found and returns the data appropriate to that type.
	/// </para>
	/// <para>
	/// In each pass, the function cracks only a single level of encryption or encoding. For additional cracking, this function, or one
	/// of the other Simplified Message Functions, must be called again.
	/// </para>
	/// </summary>
	/// <param name="dwMsgTypeFlags">
	/// <para>
	/// Indicates the message type. Message types can be combined with the bitwise- <c>OR</c> operator. This parameter can be one of the
	/// following message types:
	/// </para>
	/// <list type="bullet">
	/// <item>
	/// <term>CMSG_DATA_FLAG</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_FLAG</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED_FLAG</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED_FLAG</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED_FLAG</term>
	/// </item>
	/// </list>
	/// <para><c>Note</c> After return, the <c>DWORD</c> pointed to by pdwMsgType is set with the type of the message.</para>
	/// </param>
	/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
	/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
	/// <param name="dwSignerIndex">
	/// <para>
	/// Indicates which signer, among the possible many signers of a message, is to be verified. This index can be changed in multiple
	/// calls to the function to verify additional signers.
	/// </para>
	/// <para>
	/// dwSignerIndex is set to zero for the first signer. If the function returns <c>FALSE</c>, and GetLastError returns
	/// CRYPT_E_NO_SIGNER, the previous call returned the last signer of the message. This parameter is used only with messages of types
	/// CMSG_SIGNED_AND_ENVELOPED or CMSG_SIGNED. For all other message types, it should be set to zero.
	/// </para>
	/// </param>
	/// <param name="pbEncodedBlob">A pointer to the encoded BLOB that is to be decoded.</param>
	/// <param name="cbEncodedBlob">The size, in bytes, of the encoded BLOB.</param>
	/// <param name="dwPrevInnerContentType">
	/// Only applicable when processing nested cryptographic messages. When processing an outer cryptographic message, it must be set to
	/// zero. When decoding a nested cryptographic message, it is set to the value returned at pdwInnerContentType by a previous calling
	/// of <c>CryptDecodeMessage</c> for the outer message. It can be any of the CMSG types listed in pdwMsgType. For backward
	/// compatibility, set dwPrevInnerContentType to zero.
	/// </param>
	/// <param name="pdwMsgType">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the message type returned. This parameter can be one of the following message types:
	/// </para>
	/// <list type="bullet">
	/// <item>
	/// <term>CMSG_DATA</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pdwInnerContentType">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the type of an inner message. The message type codes used for pdwMsgType are used
	/// here, also.
	/// </para>
	/// <para>If there is no cryptographic nesting, CMSG_DATA is returned.</para>
	/// </param>
	/// <param name="pbDecoded">
	/// <para>A pointer to a buffer to receive the decoded message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the decoded message is not required or to set the size of the decoded message for memory
	/// allocation purposes. A decoded message will not be returned if this parameter is <c>NULL</c>. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecoded">
	/// <para>
	/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pbDecoded parameter. When the
	/// function returns, this variable contains the size of the decoded message.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned in the buffer, applications must use the actual size of the data returned. The
	/// actual size can be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually
	/// specified large enough to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed
	/// to by this parameter is updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="ppXchgCert">
	/// A pointer to a pointer to a CERT_CONTEXT structure with a certificate that corresponds to the private exchange key needed to
	/// decode the message. This parameter is only set for message types CMSG_ENVELOPED and CMSG_SIGNED_AND_ENVELOPED.
	/// </param>
	/// <param name="ppSignerCert">
	/// A pointer to a pointer to a CERT_CONTEXT structure of the certificate context of the signer. This parameter is only set for
	/// message types CMSG_SIGNED and CMSG_SIGNED_AND_ENVELOPED.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>The CryptDecryptMessage, CryptVerifyMessageSignature, or CryptVerifyMessageHash functions can be propagated to this function.</para>
	/// <para>The following error code is most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
	/// </term>
	/// </item>
	/// </list>
	/// </returns>
	/// <remarks>
	/// <para>
	/// The dwMsgTypeFlags parameter specifies the set of allowable messages. For example, to decode either SIGNED or ENVELOPED
	/// messages, set dwMsgTypeFlags to CMSG_SIGNED_FLAG | CMSG_ENVELOPED_FLAG. Either or both of the pDecryptPara or pVerifyPara
	/// parameters must be specified.
	/// </para>
	/// <para>
	/// For a successfully decoded or verified message, the certificate context pointers pointed to by ppXchgCert and ppSignerCert are
	/// updated. They must be freed by calling CertFreeCertificateContext. If the function fails, they are set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// The ppXchgCert or ppSignerCert parameters can be set to <c>NULL</c> before the function is called, which indicates that the
	/// caller is not interested in getting the exchange certificate or the signer certificate context.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecodemessage BOOL CryptDecodeMessage( DWORD
	// dwMsgTypeFlags, PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE
	// *pbEncodedBlob, DWORD cbEncodedBlob, DWORD dwPrevInnerContentType, DWORD *pdwMsgType, DWORD *pdwInnerContentType, BYTE
	// *pbDecoded, DWORD *pcbDecoded, PCCERT_CONTEXT *ppXchgCert, PCCERT_CONTEXT *ppSignerCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "25ffd058-8f75-4ba5-b075-e3efc09f5d9d")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptDecodeMessage(CryptMsgType dwMsgTypeFlags, in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
		uint dwSignerIndex, [In, SizeDef(nameof(cbEncodedBlob))] IntPtr pbEncodedBlob, uint cbEncodedBlob, CryptMsgType dwPrevInnerContentType,
		out CryptMsgType pdwMsgType, out CryptMsgType pdwInnerContentType, [Out, SizeDef(nameof(pcbDecoded), SizingMethod.Query)] IntPtr pbDecoded,
		ref uint pcbDecoded, out SafePCCERT_CONTEXT ppXchgCert, out SafePCCERT_CONTEXT ppSignerCert);

	/// <summary>The <c>CryptDecryptAndVerifyMessageSignature</c> function decrypts a message and verifies its signature.</summary>
	/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
	/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
	/// <param name="dwSignerIndex">
	/// Identifies a particular signer of the message. A message can be signed by more than one signer and this function can be called
	/// multiple times changing this parameter to check for several signers. It is set to zero for the first signer. If the function
	/// returns <c>FALSE</c>, and GetLastError returns CRYPT_E_NO_SIGNER, the previous call received the last signer of the message.
	/// </param>
	/// <param name="pbEncryptedBlob">A pointer to the signed, encoded, and encrypted message to be decrypted and verified.</param>
	/// <param name="cbEncryptedBlob">The size, in bytes, of the encrypted message.</param>
	/// <param name="pbDecrypted">
	/// <para>A pointer to a buffer to receive the decrypted message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the decrypted message is not required or to set the size of the decrypted message for
	/// memory allocation purposes. A decrypted message will not be returned if this parameter is <c>NULL</c>. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecrypted">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
	/// function returns, it contains the size of the decrypted message copied to pbDecrypted.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
	/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On output, the
	/// variable pointed to by this parameter is set to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="ppXchgCert">
	/// A pointer to a CERT_CONTEXT structure of the certificate that corresponds to the private exchange key needed to decrypt the message.
	/// </param>
	/// <param name="ppSignerCert">A pointer to a CERT_CONTEXT structure of the certificate of the signer.</param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptDecryptMessage and CryptVerifyMessageSignature might be propagated to this function.
	/// </para>
	/// <para>The GetLastError function returns the following error code most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
	/// </term>
	/// </item>
	/// </list>
	/// </returns>
	/// <remarks>
	/// <para>
	/// For a successfully decrypted and verified message, the certificate context pointers pointed to by ppXchgCert and ppSignerCert
	/// are updated. They must be freed by calling CertFreeCertificateContext. If the function fails, they are set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// To indicate that the caller is not interested in the exchange certificate or the signer certificate context, set the ppXchgCert
	/// and ppSignerCert parameters to <c>NULL</c>.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Sending and Receiving a Signed and Encrypted Message.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptandverifymessagesignature BOOL
	// CryptDecryptAndVerifyMessageSignature( PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD
	// dwSignerIndex, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD *pcbDecrypted, PCCERT_CONTEXT
	// *ppXchgCert, PCCERT_CONTEXT *ppSignerCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "0864a187-617f-4a21-9809-d2dbbc54ab9c")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptDecryptAndVerifyMessageSignature(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
		uint dwSignerIndex, [In, SizeDef(nameof(cbEncryptedBlob))] IntPtr pbEncryptedBlob, uint cbEncryptedBlob,
		[Out, SizeDef(nameof(pcbDecrypted), SizingMethod.Query)] IntPtr pbDecrypted, ref uint pcbDecrypted,
		out SafePCCERT_CONTEXT ppXchgCert, out SafePCCERT_CONTEXT ppSignerCert);

	/// <summary>The <c>CryptDecryptMessage</c> function decodes and decrypts a message.</summary>
	/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
	/// <param name="pbEncryptedBlob">A pointer to a buffer that contains the encoded and encrypted message to be decrypted.</param>
	/// <param name="cbEncryptedBlob">The size, in bytes, of the encoded and encrypted message.</param>
	/// <param name="pbDecrypted">
	/// <para>A pointer to a buffer that receives the decrypted message.</para>
	/// <para>
	/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. A decrypted message will
	/// not be returned if this parameter is <c>NULL</c>. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecrypted">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
	/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbDecrypted.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
	/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On input,
	/// buffer sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer. On
	/// output, the <c>DWORD</c> is updated to the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="ppXchgCert">
	/// A pointer to a CERT_CONTEXT structure of a certificate that corresponds to the private exchange key needed to decrypt the
	/// message. To indicate that the function should not return the certificate context used to decrypt, set this parameter to <c>NULL</c>.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para><c>Note</c> Errors from calls to CryptImportKey and CryptDecrypt might be propagated to this function.</para>
	/// <para>The GetLastError function returns the following error codes most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pDecryptPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not an enveloped cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was encrypted by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_DECRYPT_CERT</term>
	/// <term>No certificate was found having a private key property to use for decrypting.</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// When <c>NULL</c> is passed for pbDecrypted, and pcbDecrypted is not <c>NULL</c>, <c>NULL</c> is returned for the address passed
	/// in ppXchgCert; otherwise, a pointer to a CERT_CONTEXT is returned. For a successfully decrypted message, this pointer to a
	/// <c>CERT_CONTEXT</c> points to the certificate context used to decrypt the message. It must be freed by calling
	/// CertFreeCertificateContext. If the function fails, the value at ppXchgCert is set to <c>NULL</c>.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Using CryptEncryptMessage and CryptDecryptMessage.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptmessage BOOL CryptDecryptMessage(
	// PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD
	// *pcbDecrypted, PCCERT_CONTEXT *ppXchgCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "e540b816-64e1-4c78-9020-2b221e813acc")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptDecryptMessage(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, [In, SizeDef(nameof(cbEncryptedBlob))] IntPtr pbEncryptedBlob,
		uint cbEncryptedBlob, [Out, SizeDef(nameof(pcbDecrypted), SizingMethod.Query)] IntPtr pbDecrypted, ref uint pcbDecrypted,
		out SafePCCERT_CONTEXT ppXchgCert);

	/// <summary>The <c>CryptDecryptMessage</c> function decodes and decrypts a message.</summary>
	/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
	/// <param name="pbEncryptedBlob">A pointer to a buffer that contains the encoded and encrypted message to be decrypted.</param>
	/// <param name="cbEncryptedBlob">The size, in bytes, of the encoded and encrypted message.</param>
	/// <param name="pbDecrypted">
	/// <para>A pointer to a buffer that receives the decrypted message.</para>
	/// <para>
	/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. A decrypted message will
	/// not be returned if this parameter is <c>NULL</c>. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecrypted">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
	/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbDecrypted.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
	/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On input,
	/// buffer sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer. On
	/// output, the <c>DWORD</c> is updated to the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="ppXchgCert">
	/// A pointer to a CERT_CONTEXT structure of a certificate that corresponds to the private exchange key needed to decrypt the
	/// message. To indicate that the function should not return the certificate context used to decrypt, set this parameter to <c>NULL</c>.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para><c>Note</c> Errors from calls to CryptImportKey and CryptDecrypt might be propagated to this function.</para>
	/// <para>The GetLastError function returns the following error codes most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pDecryptPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not an enveloped cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was encrypted by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_DECRYPT_CERT</term>
	/// <term>No certificate was found having a private key property to use for decrypting.</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// When <c>NULL</c> is passed for pbDecrypted, and pcbDecrypted is not <c>NULL</c>, <c>NULL</c> is returned for the address passed
	/// in ppXchgCert; otherwise, a pointer to a CERT_CONTEXT is returned. For a successfully decrypted message, this pointer to a
	/// <c>CERT_CONTEXT</c> points to the certificate context used to decrypt the message. It must be freed by calling
	/// CertFreeCertificateContext. If the function fails, the value at ppXchgCert is set to <c>NULL</c>.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Using CryptEncryptMessage and CryptDecryptMessage.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptmessage BOOL CryptDecryptMessage(
	// PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD
	// *pcbDecrypted, PCCERT_CONTEXT *ppXchgCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "e540b816-64e1-4c78-9020-2b221e813acc")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptDecryptMessage(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, [In, SizeDef(nameof(cbEncryptedBlob))] IntPtr pbEncryptedBlob,
		uint cbEncryptedBlob, [Out, SizeDef(nameof(pcbDecrypted), SizingMethod.Query)] IntPtr pbDecrypted, ref uint pcbDecrypted, IntPtr ppXchgCert = default);

	/// <summary>The <c>CryptEncryptMessage</c> function encrypts and encodes a message.</summary>
	/// <param name="pEncryptPara">
	/// <para>A pointer to a CRYPT_ENCRYPT_MESSAGE_PARA structure that contains the encryption parameters.</para>
	/// <para>
	/// The <c>CryptEncryptMessage</c> function does not support the SHA2 OIDs, <c>szOID_DH_SINGLE_PASS_STDDH_SHA256_KDF</c> and <c>szOID_DH_SINGLE_PASS_STDDH_SHA384_KDF</c>.
	/// </para>
	/// </param>
	/// <param name="cRecipientCert">Number of elements in the rgpRecipientCert array.</param>
	/// <param name="rgpRecipientCert">
	/// Array of pointers to CERT_CONTEXT structures that contain the certificates of intended recipients of the message.
	/// </param>
	/// <param name="pbToBeEncrypted">A pointer to a buffer that contains the message that is to be encrypted.</param>
	/// <param name="cbToBeEncrypted">The size, in bytes, of the message that is to be encrypted.</param>
	/// <param name="pbEncryptedBlob">
	/// <para>A pointer to BLOB that contains a buffer that receives the encrypted and encoded message.</para>
	/// <para>
	/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbEncryptedBlob">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbEncryptedBlob parameter. When
	/// the function returns, this variable contains the size, in bytes, of the encrypted and encoded message copied to pbEncryptedBlob.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned in the buffer of the pbEncryptedBlob, applications need to use the actual size of
	/// the data returned. The actual size can be slightly smaller than the size of the buffer specified on input. (On input, buffer
	/// sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer.) On output, the
	/// variable pointed to by this parameter is updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>
	/// <c>Note</c> Errors from calls to CryptGenKey, CryptEncrypt, CryptImportKey, and CryptExportKey can be propagated to this function.
	/// </para>
	/// <para>The GetLastError function returns the following error codes most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbEncryptedBlob parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbEncryptedBlob.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pEncryptPara is not valid.
	/// </term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptencryptmessage BOOL CryptEncryptMessage(
	// PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, DWORD cRecipientCert, PCCERT_CONTEXT [] rgpRecipientCert, const BYTE *pbToBeEncrypted,
	// DWORD cbToBeEncrypted, BYTE *pbEncryptedBlob, DWORD *pcbEncryptedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "927f2e9a-96cf-4744-bd57-420b5034d28d")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptEncryptMessage(in CRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, uint cRecipientCert, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] PCCERT_CONTEXT[] rgpRecipientCert,
		[In, SizeDef(nameof(cbToBeEncrypted))] IntPtr pbToBeEncrypted, uint cbToBeEncrypted, [Out, SizeDef(nameof(pcbEncryptedBlob), SizingMethod.Query)] IntPtr pbEncryptedBlob, ref uint pcbEncryptedBlob);
	
	/// <summary>
	/// The <c>CryptGetMessageCertificates</c> function returns the handle of an open certificate store containing the message's
	/// certificates and CRLs. This function calls CertOpenStore using provider type CERT_STORE_PROV_PKCS7 as its lpszStoreProvider parameter.
	/// </summary>
	/// <param name="dwMsgAndCertEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="hCryptProv">
	/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Windows Server 2003 and Windows XP:</c> Handle of the CSP passed to CertOpenStore. For more information, see
	/// CertOpenStore.Unless there is a strong reason for passing a specific cryptographic provider in hCryptProv, pass zero to cause
	/// the default RSA or DSS provider to be acquired.
	/// </para>
	/// <para>This parameter's data type is <c>HCRYPTPROV</c>.</para>
	/// </param>
	/// <param name="dwFlags">Flags passed to CertOpenStore. For more information, see CertOpenStore.</param>
	/// <param name="pbSignedBlob">A pointer to a buffered CRYPT_INTEGER_BLOB structure that contains the signed message.</param>
	/// <param name="cbSignedBlob">The size, in bytes, of the signed message.</param>
	/// <returns>
	/// <para>Returns the certificate store containing the message's certificates and CRLs. For an error, <c>NULL</c> is returned.</para>
	/// <para>The following lists the error code most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING are supported.</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>Use GetLastError to determine the reason for any errors.</para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Setting and Getting Certificate Store Properties.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgetmessagecertificates HCERTSTORE
	// CryptGetMessageCertificates( DWORD dwMsgAndCertEncodingType, HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const BYTE
	// *pbSignedBlob, DWORD cbSignedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d890f91f-bb45-463b-b7c0-56acc9367571")]
	public static extern SafeHCERTSTORE CryptGetMessageCertificates(CertEncodingType dwMsgAndCertEncodingType, [Optional] HCRYPTPROV hCryptProv,
		CertStoreFlags dwFlags, [In, SizeDef(nameof(cbSignedBlob))] IntPtr pbSignedBlob, uint cbSignedBlob);

	/// <summary>The <c>CryptGetMessageSignerCount</c> function returns the number of signers of a signed message.</summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pbSignedBlob">A pointer to a buffer containing the signed message.</param>
	/// <param name="cbSignedBlob">The size, in bytes, of the signed message.</param>
	/// <returns>
	/// <para>Returns the number of signers of a signed message, zero when there are no signers, and minus one (–1) for an error.</para>
	/// <para>For extended error information, call GetLastError. The following error code is most commonly returned.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>Invalid message encoding type. Currently only PKCS_7_ASN_ENCODING is supported.</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgetmessagesignercount LONG
	// CryptGetMessageSignerCount( DWORD dwMsgEncodingType, const BYTE *pbSignedBlob, DWORD cbSignedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d18bda8b-b333-4b1e-8ed5-f8eff04b3168")]
	public static extern int CryptGetMessageSignerCount(CertEncodingType dwMsgEncodingType, [In, SizeDef(nameof(cbSignedBlob))] IntPtr pbSignedBlob, uint cbSignedBlob);

	/// <summary>The <c>CryptHashMessage</c> function creates a hash of the message.</summary>
	/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure that contains the hash parameters.</param>
	/// <param name="fDetachedHash">
	/// If this parameter is set to <c>TRUE</c>, only pbComputedHash is encoded in pbHashedBlob. Otherwise, both rgpbToBeHashed and
	/// pbComputedHash are encoded.
	/// </param>
	/// <param name="cToBeHashed">
	/// The number of array elements in rgpbToBeHashed and rgcbToBeHashed. This parameter can only be one unless fDetachedHash is set to <c>TRUE</c>.
	/// </param>
	/// <param name="rgpbToBeHashed">An array of pointers to buffers that contain the contents to be hashed.</param>
	/// <param name="rgcbToBeHashed">An array of sizes, in bytes, of the buffers pointed to by rgpbToBeHashed.</param>
	/// <param name="pbHashedBlob">
	/// <para>A pointer to a buffer to receive the hashed message encoded for transmission.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the hashed message is not needed for additional processing or to set the size of the hashed
	/// message for memory allocation purposes. A hashed message will not be returned if this parameter is <c>NULL</c>. For more
	/// information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbHashedBlob">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbHashedBlob parameter. When the
	/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbHashedBlob. This parameter
	/// must be the address of a <c>DWORD</c> and not <c>NULL</c> or the length of the buffer will not be returned.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. On input, buffer sizes are usually specified large enough to
	/// ensure that the largest possible output data will fit in the buffer. On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="pbComputedHash">
	/// A pointer to a buffer to receive the newly created hash value. This parameter can be <c>NULL</c> if the newly created hash is
	/// not needed for additional processing, or to set the size of the hash for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </param>
	/// <param name="pcbComputedHash">
	/// <para>
	/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbComputedHash parameter. When
	/// the function returns, this <c>DWORD</c> contains the size, in bytes, of the newly created hash that was copied to pbComputedHash.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. On input, buffer sizes are usually specified large enough to
	/// ensure that the largest possible output data will fit in the buffer. On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this function.
	/// </para>
	/// <para>The GetLastError function returns the following error codes most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbHashedBlob parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, into the variable pointed to by pbHashedBlob.
	/// </term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-crypthashmessage BOOL CryptHashMessage(
	// PCRYPT_HASH_MESSAGE_PARA pHashPara, BOOL fDetachedHash, DWORD cToBeHashed, const BYTE * [] rgpbToBeHashed, DWORD []
	// rgcbToBeHashed, BYTE *pbHashedBlob, DWORD *pcbHashedBlob, BYTE *pbComputedHash, DWORD *pcbComputedHash );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "85a04c01-fd7c-4d87-b6e1-a0f2aea45d16")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptHashMessage(in CRYPT_HASH_MESSAGE_PARA pHashPara, [MarshalAs(UnmanagedType.Bool)] bool fDetachedHash, uint cToBeHashed,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] rgpbToBeHashed, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] uint[] rgcbToBeHashed,
		[Out, SizeDef(nameof(pcbHashedBlob), SizingMethod.Query)] IntPtr pbHashedBlob, ref uint pcbHashedBlob,
		[Out, Optional, SizeDef(nameof(pcbComputedHash), SizingMethod.Query)] IntPtr pbComputedHash, ref uint pcbComputedHash);

	/// <summary>
	/// The <c>CryptMsgCalculateEncodedLength</c> function calculates the maximum number of bytes needed for an encoded cryptographic
	/// message given the message type, encoding parameters, and total length of the data to be encoded. Note that the result will
	/// always be greater than or equal to the actual number of bytes needed.
	/// </summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwFlags">
	/// <para>Currently defined flags are shown in the following table.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_BARE_CONTENT_FLAG</term>
	/// <term>
	/// Indicates that streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be
	/// streamed into an enclosing message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_DETACHED_FLAG</term>
	/// <term>Indicates that there is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
	/// <term>
	/// Used to calculate the size of a DER encoding of a message to be nested inside an enveloped message. This is particularly useful
	/// when streaming is being performed.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
	/// <term>
	/// Non-Data type inner content is encapsulated within an OCTET STRING. This flag is applicable for both Signed and Enveloped messages.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwMsgType">
	/// <para>Currently defined message types are shown in the following table.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DATA</term>
	/// <term>An octet (BYTE) string.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// <term>CMSG_SIGNED_ENCODE_INFO</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// <term>CMSG_ENVELOPED_ENCODE_INFO</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// <term>Not implemented.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// <term>CMSG_HASHED_ENCODE_INFO</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENCRYPTED</term>
	/// <term>Not implemented.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvMsgEncodeInfo">
	/// A pointer to the data to be encoded. The type of data pointed to depends on the value of dwMsgType. For details, see the
	/// dwMsgType table.
	/// </param>
	/// <param name="pszInnerContentObjID">
	/// <para>
	/// When calling <c>CryptMsgCalculateEncodedLength</c> with data provided to CryptMsgUpdate already encoded, the appropriate object
	/// identifier is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, the inner content type is assumed not to
	/// have been previously encoded, and is encoded as an octet string and given the type CMSG_DATA.
	/// </para>
	/// <para>When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
	/// <para>The following algorithm object identifiers are commonly used:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>szOID_RSA_data</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_envelopedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signEnvData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_digestedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_encryptedData</term>
	/// </item>
	/// <item>
	/// <term>SPC_INDIRECT_DATA_OBJID</term>
	/// </item>
	/// </list>
	/// <para>
	/// A user can define new inner content usage. The user must ensure that the sender and receiver of the message agree upon the
	/// semantics associated with the object identifier.
	/// </para>
	/// </param>
	/// <param name="cbData">The size, in bytes, of the content.</param>
	/// <returns>
	/// <para>
	/// Returns the required length for an encoded cryptographic message. This length might not be the exact length but it will not be
	/// less than the required length. Zero is returned if the function fails.
	/// </para>
	/// <para>
	/// To retrieve extended error information, use the GetLastError function. The following table lists the error codes most commonly returned.
	/// </para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcalculateencodedlength DWORD
	// CryptMsgCalculateEncodedLength( DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR
	// pszInnerContentObjID, DWORD cbData );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
	public static extern uint CryptMsgCalculateEncodedLength(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType,
		[In] IntPtr pvMsgEncodeInfo, [Optional] SafeOID pszInnerContentObjID, uint cbData);

	/// <summary>
	/// The <c>CryptMsgClose</c> function closes a cryptographic message handle. At each call to this function, the reference count on
	/// the message is reduced by one. When the reference count reaches zero, the message is fully released.
	/// </summary>
	/// <param name="hCryptMsg">Handle of the cryptographic message to be closed.</param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgclose BOOL CryptMsgClose( HCRYPTMSG hCryptMsg );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "2478dd60-233a-4ef3-86e9-62d2a59ab28a")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgClose(HCRYPTMSG hCryptMsg);

	/// <summary>
	/// <para>
	/// The <c>CryptMsgControl</c> function performs a control operation after a message has been decoded by a final call to the
	/// CryptMsgUpdate function. The control operations provided by this function are used for decryption, signature and hash
	/// verification, and the addition and deletion of certificates, certificate revocation lists (CRLs), signers, and unauthenticated attributes.
	/// </para>
	/// <para>
	/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
	/// Internet Mail Extensions (S/MIME) email interoperability. For more information, see the Remarks for the CryptMsgOpenToEncode function.
	/// </para>
	/// </summary>
	/// <param name="hCryptMsg">A handle of a cryptographic message for which a control is to be applied.</param>
	/// <param name="dwFlags">
	/// <para>The following value is defined when the dwCtrlType parameter is one of the following:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CMSG_CTRL_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT</term>
	/// </item>
	/// </list>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// The handle to the cryptographic provider is released on the final call to the CryptMsgClose function. This handle is not
	/// released if the CryptMsgControl function fails.
	/// </term>
	/// </item>
	/// </list>
	/// <para>If the dwCtrlType parameter does not specify a decrypt operation, set this value to zero.</para>
	/// </param>
	/// <param name="dwCtrlType">
	/// <para>
	/// The type of operation to be performed. Currently defined message control types and the type of structure that should be passed
	/// to the pvCtrlPara parameter are shown in the following table.
	/// </para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_CTRL_ADD_ATTR_CERT 14 (0xE)</term>
	/// <term>A BLOB that contains the encoded bytes of attribute certificate.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CERT 10 (0xA)</term>
	/// <term>A CRYPT_INTEGER_BLOB structure that contains the encoded bytes of the certificate to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CMS_SIGNER_INFO 20 (0x14)</term>
	/// <term>
	/// A CMSG_CMS_SIGNER_INFO structure that contains signer information. This operation differs from CMSG_CTRL_ADD_SIGNER because the
	/// signer information contains the signature.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CRL 12 (0xC)</term>
	/// <term>A BLOB that contains the encoded bytes of the CRL to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER 6 (0x6)</term>
	/// <term>A CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR 8 (0x8)</term>
	/// <term>
	/// A CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA structure that contains the index of the signer and a BLOB that contains the
	/// unauthenticated attribute information to be added to the message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DECRYPT 2 (0x2)</term>
	/// <term>
	/// A CMSG_CTRL_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. This value is
	/// applicable to RSA recipients. This operation specifies that the CryptMsgControl function search the recipient index to obtain
	/// the key transport recipient information. If the function fails, GetLastError will return CRYPT_E_INVALID_INDEX if no key
	/// transport recipient is found.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_ATTR_CERT 15 (0xF)</term>
	/// <term>The index of the attribute certificate to be removed.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CERT 11 (0xB)</term>
	/// <term>The index of the certificate to be deleted from the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CRL 13 (0xD)</term>
	/// <term>The index of the CRL to be deleted from the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER 7 (0x7)</term>
	/// <term>The index of the signer to be deleted.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR 9 (0x9)</term>
	/// <term>
	/// A CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA structure that contains an index that specifies the signer and the index that specifies
	/// the signer's unauthenticated attribute to be deleted.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ENABLE_STRONG_SIGNATURE 21 (0x15)</term>
	/// <term>
	/// A CERT_STRONG_SIGN_PARA structure used to perform strong signature checking. To check for a strong signature, specify this
	/// control type before calling CryptMsgGetAndVerifySigner or before calling CryptMsgControl with the following control types set:
	/// After the signature is successfully verified, this function checks for a strong signature. If the signature is not strong, the
	/// operation will fail and the GetLastError value will be set to NTE_BAD_ALGID.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT 17 (0x11)</term>
	/// <term>
	/// A CMSG_CTRL_KEY_AGREE_DECRYPT_PARA structure used to decrypt the message for the specified key agreement session key. Key
	/// agreement is used with Diffie-Hellman encryption/decryption.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT 16 (0x10)</term>
	/// <term>
	/// A CMSG_CTRL_KEY_TRANS_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. Key
	/// transport is used with RSA encryption/decryption.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT 18 (0x12)</term>
	/// <term>
	/// A CMSG_CTRL_MAIL_LIST_DECRYPT_PARA structure used to decrypt the message for the specified recipient using a previously
	/// distributed key-encryption key (KEK).
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_HASH 5 (0x5)</term>
	/// <term>This value is not used.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_SIGNATURE 1 (0x1)</term>
	/// <term>A CERT_INFO structure that identifies the signer of the message whose signature is to be verified.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_SIGNATURE_EX 19 (0x13)</term>
	/// <term>
	/// A CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA structure that specifies the signer index and public key to verify the message signature.
	/// The signer public key can be a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a certificate chain context.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvCtrlPara">
	/// <para>A pointer to a structure determined by the value of dwCtrlType.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>dwCtrlType value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>
	/// CMSG_CTRL_DECRYPT, CMSG_CTRL_KEY_TRANS_DECRYPT, CMSG_CTRL_KEY_AGREE_DECRYPT, or CMSG_CTRL_MAIL_LIST_DECRYPT, and the streamed
	/// enveloped message is being decoded
	/// </term>
	/// <term>
	/// Decoding will be done as if the streamed content were being decrypted. If any encrypted streamed content has accumulated prior
	/// to this call, some or all of the plaintext that results from the decryption of the cipher text is passed back to the application
	/// through the callback function specified in the call to the CryptMsgOpenToDecode function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_HASH</term>
	/// <term>The hash computed from the content of the message is compared against the hash contained in the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER</term>
	/// <term>pvCtrlPara points to a CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER</term>
	/// <term>
	/// After a deletion is made, any other signer indices in use for this message are no longer valid and must be reacquired by calling
	/// the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR</term>
	/// <term>
	/// After a deletion is made, any other unauthenticated attribute indices in use for this signer are no longer valid and must be
	/// reacquired by calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CERT</term>
	/// <term>
	/// After a deletion is made, any other certificate indices in use for this message are no longer valid and must be reacquired by
	/// calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CRL</term>
	/// <term>
	/// After a deletion is made, any other CRL indices in use for this message are no longer valid and will need to be reacquired by
	/// calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero.</para>
	/// <para>
	/// If the function fails, the return value is zero and the GetLastError function returns an Abstract Syntax Notation One (ASN.1)
	/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// <para>
	/// When a streamed, enveloped message is being decoded, errors encountered in the application-defined callback function specified
	/// by the pStreamInfo parameter of the CryptMsgOpenToDecode function might be propagated to the <c>CryptMsgControl</c> function. If
	/// this happens, the SetLastError function is not called by the <c>CryptMsgControl</c> function after the callback function
	/// returns. This preserves any errors encountered under the control of the application. It is the responsibility of the callback
	/// function (or one of the APIs that it calls) to call the <c>SetLastError</c> function if an error occurs while the application is
	/// processing the streamed data.
	/// </para>
	/// <para>Propagated errors might be encountered from the following functions:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// <item>
	/// <term>CryptDecrypt</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptGetUserKey</term>
	/// </item>
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptImportKey</term>
	/// </item>
	/// <item>
	/// <term>CryptSignHash</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifySignature</term>
	/// </item>
	/// </list>
	/// <para>The following error codes are most commonly returned.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_ALREADY_DECRYPTED</term>
	/// <term>The message content has already been decrypted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
	/// <term>
	/// The message does not contain an expected authenticated attribute. This error can be returned if the dwCtrlType parameter is set
	/// to CMSG_CTRL_VERIFY_SIGNATURE.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_BAD_ENCODE</term>
	/// <term>
	/// An error was encountered while encoding or decoding. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_CONTROL_TYPE</term>
	/// <term>The control type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_HASH_VALUE</term>
	/// <term>The hash value is incorrect.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_INDEX</term>
	/// <term>The index value is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The object identifier is badly formatted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_ADD_SIGNER.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_RECIPIENT_NOT_FOUND</term>
	/// <term>
	/// The enveloped data message does not contain the specified recipient. This error can be returned if the dwCtrlType parameter is
	/// set to CMSG_CTRL_DECRYPT.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_SIGNER_NOT_FOUND</term>
	/// <term>The specified signer for the message was not found. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Not enough memory was available to complete the operation.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcontrol BOOL CryptMsgControl( HCRYPTMSG
	// hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "a990d44d-2993-429f-b817-2a834105ecef")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgControl([In, AddAsMember] HCRYPTMSG hCryptMsg, CryptMsgFlags dwFlags, CryptMsgControlType dwCtrlType, [In, Optional] IntPtr pvCtrlPara);

	/// <summary>
	/// <para>
	/// The <c>CryptMsgControl</c> function performs a control operation after a message has been decoded by a final call to the
	/// CryptMsgUpdate function. The control operations provided by this function are used for decryption, signature and hash
	/// verification, and the addition and deletion of certificates, certificate revocation lists (CRLs), signers, and unauthenticated attributes.
	/// </para>
	/// <para>
	/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
	/// Internet Mail Extensions (S/MIME) email interoperability. For more information, see the Remarks for the CryptMsgOpenToEncode function.
	/// </para>
	/// </summary>
	/// <typeparam name="T">The type of the control parameter.</typeparam>
	/// <param name="hCryptMsg">A handle of a cryptographic message for which a control is to be applied.</param>
	/// <param name="dwFlags">
	/// <para>The following value is defined when the dwCtrlType parameter is one of the following:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CMSG_CTRL_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT</term>
	/// </item>
	/// </list>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// The handle to the cryptographic provider is released on the final call to the CryptMsgClose function. This handle is not
	/// released if the CryptMsgControl function fails.
	/// </term>
	/// </item>
	/// </list>
	/// <para>If the dwCtrlType parameter does not specify a decrypt operation, set this value to zero.</para>
	/// </param>
	/// <param name="dwCtrlType">
	/// <para>
	/// The type of operation to be performed. Currently defined message control types and the type of structure that should be passed
	/// to the pvCtrlPara parameter are shown in the following table.
	/// </para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_CTRL_ADD_ATTR_CERT 14 (0xE)</term>
	/// <term>A BLOB that contains the encoded bytes of attribute certificate.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CERT 10 (0xA)</term>
	/// <term>A CRYPT_INTEGER_BLOB structure that contains the encoded bytes of the certificate to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CMS_SIGNER_INFO 20 (0x14)</term>
	/// <term>
	/// A CMSG_CMS_SIGNER_INFO structure that contains signer information. This operation differs from CMSG_CTRL_ADD_SIGNER because the
	/// signer information contains the signature.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_CRL 12 (0xC)</term>
	/// <term>A BLOB that contains the encoded bytes of the CRL to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER 6 (0x6)</term>
	/// <term>A CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR 8 (0x8)</term>
	/// <term>
	/// A CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA structure that contains the index of the signer and a BLOB that contains the
	/// unauthenticated attribute information to be added to the message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DECRYPT 2 (0x2)</term>
	/// <term>
	/// A CMSG_CTRL_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. This value is
	/// applicable to RSA recipients. This operation specifies that the CryptMsgControl function search the recipient index to obtain
	/// the key transport recipient information. If the function fails, GetLastError will return CRYPT_E_INVALID_INDEX if no key
	/// transport recipient is found.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_ATTR_CERT 15 (0xF)</term>
	/// <term>The index of the attribute certificate to be removed.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CERT 11 (0xB)</term>
	/// <term>The index of the certificate to be deleted from the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CRL 13 (0xD)</term>
	/// <term>The index of the CRL to be deleted from the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER 7 (0x7)</term>
	/// <term>The index of the signer to be deleted.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR 9 (0x9)</term>
	/// <term>
	/// A CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA structure that contains an index that specifies the signer and the index that specifies
	/// the signer's unauthenticated attribute to be deleted.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ENABLE_STRONG_SIGNATURE 21 (0x15)</term>
	/// <term>
	/// A CERT_STRONG_SIGN_PARA structure used to perform strong signature checking. To check for a strong signature, specify this
	/// control type before calling CryptMsgGetAndVerifySigner or before calling CryptMsgControl with the following control types set:
	/// After the signature is successfully verified, this function checks for a strong signature. If the signature is not strong, the
	/// operation will fail and the GetLastError value will be set to NTE_BAD_ALGID.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT 17 (0x11)</term>
	/// <term>
	/// A CMSG_CTRL_KEY_AGREE_DECRYPT_PARA structure used to decrypt the message for the specified key agreement session key. Key
	/// agreement is used with Diffie-Hellman encryption/decryption.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT 16 (0x10)</term>
	/// <term>
	/// A CMSG_CTRL_KEY_TRANS_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. Key
	/// transport is used with RSA encryption/decryption.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT 18 (0x12)</term>
	/// <term>
	/// A CMSG_CTRL_MAIL_LIST_DECRYPT_PARA structure used to decrypt the message for the specified recipient using a previously
	/// distributed key-encryption key (KEK).
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_HASH 5 (0x5)</term>
	/// <term>This value is not used.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_SIGNATURE 1 (0x1)</term>
	/// <term>A CERT_INFO structure that identifies the signer of the message whose signature is to be verified.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_SIGNATURE_EX 19 (0x13)</term>
	/// <term>
	/// A CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA structure that specifies the signer index and public key to verify the message signature.
	/// The signer public key can be a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a certificate chain context.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvCtrlPara">
	/// <para>A pointer to a structure determined by the value of dwCtrlType.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>dwCtrlType value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>
	/// CMSG_CTRL_DECRYPT, CMSG_CTRL_KEY_TRANS_DECRYPT, CMSG_CTRL_KEY_AGREE_DECRYPT, or CMSG_CTRL_MAIL_LIST_DECRYPT, and the streamed
	/// enveloped message is being decoded
	/// </term>
	/// <term>
	/// Decoding will be done as if the streamed content were being decrypted. If any encrypted streamed content has accumulated prior
	/// to this call, some or all of the plaintext that results from the decryption of the cipher text is passed back to the application
	/// through the callback function specified in the call to the CryptMsgOpenToDecode function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_VERIFY_HASH</term>
	/// <term>The hash computed from the content of the message is compared against the hash contained in the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_ADD_SIGNER</term>
	/// <term>pvCtrlPara points to a CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER</term>
	/// <term>
	/// After a deletion is made, any other signer indices in use for this message are no longer valid and must be reacquired by calling
	/// the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR</term>
	/// <term>
	/// After a deletion is made, any other unauthenticated attribute indices in use for this signer are no longer valid and must be
	/// reacquired by calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CERT</term>
	/// <term>
	/// After a deletion is made, any other certificate indices in use for this message are no longer valid and must be reacquired by
	/// calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CTRL_DEL_CRL</term>
	/// <term>
	/// After a deletion is made, any other CRL indices in use for this message are no longer valid and will need to be reacquired by
	/// calling the CryptMsgGetParam function.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero.</para>
	/// <para>
	/// If the function fails, the return value is zero and the GetLastError function returns an Abstract Syntax Notation One (ASN.1)
	/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// <para>
	/// When a streamed, enveloped message is being decoded, errors encountered in the application-defined callback function specified
	/// by the pStreamInfo parameter of the CryptMsgOpenToDecode function might be propagated to the <c>CryptMsgControl</c> function. If
	/// this happens, the SetLastError function is not called by the <c>CryptMsgControl</c> function after the callback function
	/// returns. This preserves any errors encountered under the control of the application. It is the responsibility of the callback
	/// function (or one of the APIs that it calls) to call the <c>SetLastError</c> function if an error occurs while the application is
	/// processing the streamed data.
	/// </para>
	/// <para>Propagated errors might be encountered from the following functions:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// <item>
	/// <term>CryptDecrypt</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptGetUserKey</term>
	/// </item>
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptImportKey</term>
	/// </item>
	/// <item>
	/// <term>CryptSignHash</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifySignature</term>
	/// </item>
	/// </list>
	/// <para>The following error codes are most commonly returned.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_ALREADY_DECRYPTED</term>
	/// <term>The message content has already been decrypted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
	/// <term>
	/// The message does not contain an expected authenticated attribute. This error can be returned if the dwCtrlType parameter is set
	/// to CMSG_CTRL_VERIFY_SIGNATURE.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_BAD_ENCODE</term>
	/// <term>
	/// An error was encountered while encoding or decoding. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_CONTROL_TYPE</term>
	/// <term>The control type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_HASH_VALUE</term>
	/// <term>The hash value is incorrect.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_INDEX</term>
	/// <term>The index value is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The object identifier is badly formatted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_ADD_SIGNER.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_RECIPIENT_NOT_FOUND</term>
	/// <term>
	/// The enveloped data message does not contain the specified recipient. This error can be returned if the dwCtrlType parameter is
	/// set to CMSG_CTRL_DECRYPT.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_SIGNER_NOT_FOUND</term>
	/// <term>The specified signer for the message was not found. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Not enough memory was available to complete the operation.</term>
	/// </item>
	/// </list>
	/// </returns>
	[PInvokeData("wincrypt.h", MSDNShortId = "a990d44d-2993-429f-b817-2a834105ecef")]
	public static bool CryptMsgControl<T>([In, AddAsMember] HCRYPTMSG hCryptMsg, CryptMsgFlags dwFlags, CryptMsgControlType dwCtrlType, in T pvCtrlPara) where T : struct
	{
		using SafeCoTaskMemStruct<T> cp = pvCtrlPara;
		return CryptMsgControl(hCryptMsg, dwFlags, dwCtrlType, cp);
	}

	/// <summary>
	/// The <c>CryptMsgCountersign</c> function countersigns an existing signature in a message. Countersignatures are used to sign an
	/// existing signature's encrypted hash of the message. Countersignatures can be used for various purposes including time stamping a message.
	/// </summary>
	/// <param name="hCryptMsg">Cryptographic message handle to be used.</param>
	/// <param name="dwIndex">Zero-based index of the signer in the signed or signed-and-enveloped message to be countersigned.</param>
	/// <param name="cCountersigners">Number of countersigners in the rgCountersigners array.</param>
	/// <param name="rgCountersigners">Array of countersigners' CMSG_SIGNER_ENCODE_INFO structures.</param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>An error can be propagated from CryptMsgCountersignEncoded.</para>
	/// <para>The following error codes are returned most often.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Ran out of memory.</term>
	/// </item>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>The specified area is not large enough to hold the returned data.</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcountersign BOOL CryptMsgCountersign( HCRYPTMSG
	// hCryptMsg, DWORD dwIndex, DWORD cCountersigners, PCMSG_SIGNER_ENCODE_INFO rgCountersigners );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "ebf76664-bca6-462d-b519-2b60f435d8ef")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgCountersign([In, AddAsMember] HCRYPTMSG hCryptMsg, uint dwIndex, uint cCountersigners, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] CMSG_SIGNER_ENCODE_INFO[] rgCountersigners);

	/// <summary>
	/// The <c>CryptMsgCountersignEncoded</c> function countersigns an existing PKCS #7 message signature. The pbCountersignature
	/// <c>BYTE</c> buffer it creates is a PKCS #7 encoded SignerInfo that can be used as an unauthenticated Countersignature attribute
	/// of a PKCS #9 signed-data or signed-and-enveloped-data message.
	/// </summary>
	/// <param name="dwEncodingType">
	/// <para>
	/// Specifies the encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional
	/// encoding types may be added in the future. For either current encoding type, use:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
	/// </param>
	/// <param name="pbSignerInfo">A pointer to the encoded SignerInfo that is to be countersigned.</param>
	/// <param name="cbSignerInfo">Count, in bytes, of the encoded SignerInfo data.</param>
	/// <param name="cCountersigners">Number of countersigners in the rgCountersigners array.</param>
	/// <param name="rgCountersigners">Array of countersigners' CMSG_SIGNER_ENCODE_INFO structures.</param>
	/// <param name="pbCountersignature">
	/// <para>A pointer to a buffer to receive an encoded PKCS #9 countersignature attribute.</para>
	/// <para>
	/// On input, this parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more
	/// information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbCountersignature">
	/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pbCountersignature parameter. When
	/// the function returns, the variable pointed to by the pcbCountersignature parameter contains the number of bytes stored in the buffer.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The object identifier is badly formatted.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Ran out of memory.</term>
	/// </item>
	/// </list>
	/// <para>Propagated errors might be returned from one of the following functions:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptSignHash</term>
	/// </item>
	/// <item>
	/// <term>CryptMsgOpenToEncode</term>
	/// </item>
	/// <item>
	/// <term>CryptMsgUpdate</term>
	/// </item>
	/// <item>
	/// <term>CryptMsgControl</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcountersignencoded BOOL
	// CryptMsgCountersignEncoded( DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD cbSignerInfo, DWORD cCountersigners,
	// PCMSG_SIGNER_ENCODE_INFO rgCountersigners, PBYTE pbCountersignature, PDWORD pcbCountersignature );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d9fd734b-e14d-4392-ac88-5565aefbedb4")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgCountersignEncoded(CertEncodingType dwEncodingType, [In, SizeDef(nameof(cbSignerInfo))] IntPtr pbSignerInfo,
		uint cbSignerInfo, uint cCountersigners, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] CMSG_SIGNER_ENCODE_INFO[] rgCountersigners,
		[Out, SizeDef(nameof(pcbCountersignature), SizingMethod.Query)] IntPtr pbCountersignature, ref uint pcbCountersignature);

	/// <summary>The <c>CryptMsgDuplicate</c> function duplicates a cryptographic message handle by incrementing its reference count.</summary>
	/// <param name="hCryptMsg">
	/// Handle of the cryptographic message to be duplicated. Duplication is done by incrementing the reference count of the message. A
	/// copy of the message is not made.
	/// </param>
	/// <returns>
	/// The returned handle is the same as the handle input. A copy of the message is not created. When you have finished using the
	/// duplicated message handle, decrease the reference count by calling the CryptMsgClose function.
	/// </returns>
	/// <remarks>
	/// <para>
	/// <c>CryptMsgDuplicate</c> is used to increase the reference count on an <c>HCRYPTMSG</c> handle so that multiple calls to
	/// CryptMsgClose are required to actually release the handle.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a Hashed Message.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgduplicate HCRYPTMSG CryptMsgDuplicate( HCRYPTMSG
	// hCryptMsg );
	[DllImport(Lib.Crypt32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "9b1142b9-0caa-4304-bfe6-1c27c6a7b782")]
	public static extern SafeHCRYPTMSG CryptMsgDuplicate(HCRYPTMSG hCryptMsg);

	/// <summary>
	/// The <c>CryptMsgGetParam</c> function acquires a message parameter after a cryptographic message has been encoded or decoded.
	/// This function is called after the final CryptMsgUpdate call.
	/// </summary>
	/// <param name="hCryptMsg">Handle of a cryptographic message.</param>
	/// <param name="dwParamType">
	/// <para>
	/// Indicates the parameter types of data to be retrieved. The type of data to be retrieved determines the type of structure to use
	/// for pvData.
	/// </para>
	/// <para>
	/// For an encoded message, only the CMSG_BARE_CONTENT, CMSG_ENCODE_SIGNER, CMSG_CONTENT_PARAM and CMSG_COMPUTED_HASH_PARAM
	/// dwParamTypes are valid.
	/// </para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_ATTR_CERT_COUNT_PARAM</term>
	/// <term>pvData data type: pointer to a DWORD Returns the count of the attribute certificates in a SIGNED or ENVELOPED message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ATTR_CERT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Retrieves an attribute certificate. To get all the attribute certificates, call
	/// CryptMsgGetParam varying dwIndex set to 0 the number of attributes minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_BARE_CONTENT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Retrieves the encoded content of an encoded cryptographic message, without the outer
	/// layer of the CONTENT_INFO structure. That is, only the encoding of the PKCS #7 defined ContentInfo.content field is returned.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CERT_COUNT_PARAM</term>
	/// <term>pvData data type: pointer to DWORD Returns the number of certificates in a received SIGNED or ENVELOPED message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CERT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Returns a signer's certificate. To get all of the signer's certificates, call
	/// CryptMsgGetParam, varying dwIndex from 0 to the number of available certificates minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_COMPUTED_HASH_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Returns the hash calculated of the data in the message. This type is applicable to
	/// both encode and decode.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CONTENT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Returns the whole PKCS #7 message from a message opened to encode. Retrieves the inner
	/// content of a message opened to decode. If the message is enveloped, the inner type is data, and CryptMsgControl has been called
	/// to decrypt the message, the decrypted content is returned. If the inner type is not data, the encoded BLOB that requires further
	/// decoding is returned. If the message is not enveloped and the inner content is DATA, the returned data is the octets of the
	/// inner content. This type is applicable to both encode and decode. For decoding, if the type is CMSG_DATA, the content's octets
	/// are returned; else, the encoded inner content is returned.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRL_COUNT_PARAM</term>
	/// <term>pvData data type: pointer to DWORD Returns the count of CRLs in a received, SIGNED or ENVELOPED message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRL_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Returns a CRL. To get all the CRLs, call CryptMsgGetParam, varying dwIndex from 0 to
	/// the number of available CRLs minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENCODED_MESSAGE</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array Changes the contents of an already encoded message. The message must first be decoded
	/// with a call to CryptMsgOpenToDecode. Then the change to the message is made through a call to CryptMsgControl,
	/// CryptMsgCountersign, or CryptMsgCountersignEncoded. The message is then encoded again with a call to CryptMsgGetParam,
	/// specifying CMSG_ENCODED_MESSAGE to get a new encoding that reflects the changes made. This can be used, for instance, to add a
	/// time-stamp attribute to a message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENCODED_SIGNER</term>
	/// <term>pvData data type: pointer to a BYTE array Returns the encoded CMSG_SIGNER_INFO signer information for a message signer.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENCRYPTED_DIGEST</term>
	/// <term>pvData data type: pointer to a BYTE array Returns the encrypted hash of a signature. Typically used for performing time-stamping.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENCRYPT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the encryption algorithm used to
	/// encrypted the message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPE_ALGORITHM_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the encryption algorithm used to
	/// encrypt an ENVELOPED message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASH_ALGORITHM_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the hash algorithm used to hash
	/// the message when it was created.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASH_DATA_PARAM</term>
	/// <term>pvData data type: pointer to a BYTE array Returns the hash value stored in the message when it was created.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_INNER_CONTENT_TYPE_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a null-terminated object identifier (OID) string. Returns the inner content
	/// type of a received message. This type is not applicable to messages of type DATA.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_RECIPIENT_COUNT_PARAM</term>
	/// <term>pvData data type: pointer to a DWORD Returns the number of key transport recipients of an ENVELOPED received message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_RECIPIENT_COUNT_PARAM</term>
	/// <term>
	/// pvData data type: pointer to DWORD Returns the total count of all message recipients including key agreement and mail list recipients.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_RECIPIENT_INDEX_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a DWORD Returns the index of the key transport recipient used to decrypt an ENVELOPED message. This
	/// value is available only after a message has been decrypted.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_RECIPIENT_INDEX_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a DWORD Returns the index of the key transport, key agreement, or mail list recipient used to
	/// decrypt an ENVELOPED message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a DWORD Returns the index of the encrypted key of a key agreement recipient used to decrypt an
	/// ENVELOPED message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_RECIPIENT_INFO_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CERT_INFO structure. Returns certificate information about a key
	/// transport message's recipient. To get certificate information on all key transport message's recipients, repetitively call
	/// CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus one. Only the Issuer, SerialNumber, and
	/// PublicKeyAlgorithm members of the CERT_INFO structure returned are available and valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_RECIPIENT_INFO_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CMSG_CMS_RECIPIENT_INFO structure. Returns information about a key
	/// transport, key agreement, or mail list recipient. It is not limited to key transport message recipients. To get information on
	/// all of a message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_AUTH_ATTR_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CRYPT_ATTRIBUTES structure. Returns the authenticated attributes of a
	/// message signer. To retrieve the authenticated attributes for a specified signer, call CryptMsgGetParam with dwIndex equal to
	/// that signer's index.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_CERT_INFO_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive the CERT_INFO structure. Returns information on a message signer needed to
	/// identify the signer's certificate. A certificate's Issuer and SerialNumber can be used to uniquely identify a certificate for
	/// retrieval. To retrieve information for all the signers, repetitively call CryptMsgGetParam varying dwIndex from 0 to the number
	/// of signers minus one. Only the Issuer and SerialNumber fields in the CERT_INFO structure returned contain available, valid data.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_CERT_ID_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CERT_ID structure. Returns information on a message signer needed to
	/// identify the signer's public key. This could be a certificate's Issuer and SerialNumber, a KeyID, or a HashId. To retrieve
	/// information for all the signers, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_COUNT_PARAM</term>
	/// <term>pvData data type: pointer to a DWORD Returns the number of signers of a received SIGNED message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_HASH_ALGORITHM_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive the CRYPT_ALGORITHM_IDENTIFIER structure. Returns the hash algorithm used
	/// by a signer of the message. To get the hash algorithm for a specified signer, call CryptMsgGetParam with dwIndex equal to that
	/// signer's index.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_INFO_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CMSG_SIGNER_INFO structure. Returns information on a message signer. This
	/// includes the issuer and serial number of the signer's certificate and authenticated and unauthenticated attributes of the
	/// signer's certificate. To retrieve signer information on all of the signers of a message, call CryptMsgGetParam varying dwIndex
	/// from 0 to the number of signers minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_SIGNER_INFO_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CMSG_CMS_SIGNER_INFO structure. Returns information on a message signer.
	/// This includes a signerId and authenticated and unauthenticated attributes. To retrieve signer information on all of the signers
	/// of a message, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_UNAUTH_ATTR_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CRYPT_ATTRIBUTES structure. Returns a message signer's unauthenticated
	/// attributes. To retrieve the unauthenticated attributes for a specified signer, call CryptMsgGetParam with dwIndex equal to that
	/// signer's index.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_TYPE_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a DWORD Returns the message type of a decoded message of unknown type. The retrieved message type
	/// can be compared to supported types to determine whether processing can continued. For supported message types, see the
	/// dwMessageType parameter of CryptMsgOpenToDecode.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_UNPROTECTED_ATTR_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a BYTE array to receive a CMSG_ATTR structure. Returns the unprotected attributes in an enveloped message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_VERSION_PARAM</term>
	/// <term>
	/// pvData data type: pointer to a DWORD Returns the version of the decoded message. For more information, see the table in the
	/// Remarks section.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwIndex">
	/// Index for the parameter being retrieved, where applicable. When a parameter is not being retrieved, this parameter is ignored
	/// and is set to zero.
	/// </param>
	/// <param name="pvData">
	/// <para>
	/// A pointer to a buffer that receives the data retrieved. The form of this data will vary depending on the value of the
	/// dwParamType parameter.
	/// </para>
	/// <para>
	/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// <para>
	/// When processing the data returned in this buffer, applications need to use the actual size of the data returned. The actual size
	/// can be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large
	/// enough to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this
	/// parameter is updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="pcbData">
	/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pvData parameter. When the function
	/// returns, the variable pointed to by the pcbData parameter contains the number of bytes stored in the buffer.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero (FALSE). For extended error information, call GetLastError.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_ATTRIBUTES_MISSING</term>
	/// <term>The message does not contain the requested attributes.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_INDEX</term>
	/// <term>The index value is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NOT_DECRYPTED</term>
	/// <term>The message content has not been decrypted yet.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The object identifier is badly formatted.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>The specified buffer is not large enough to hold the returned data.</term>
	/// </item>
	/// </list>
	/// <para>For dwParamType CMSG_COMPUTED_HASH_PARAM, an error can be propagated from CryptGetHashParam.</para>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// The following version numbers are returned by calls to <c>CryptMsgGetParam</c> with dwParamType set to CMSG_VERSION_PARAM are defined:
	/// </para>
	/// <list type="bullet">
	/// <item>
	/// <term>CMSG_SIGNED_DATA_V1</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_DATA_V3</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_DATA_PKCS_1_5_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_DATA_CMS_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_INFO_V1</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_INFO_V3</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_INFO_PKCS_1_5_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNER_INFO_CMS_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED_DATA_V0</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED_DATA_V2</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED_DATA_PKCS_1_5_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED_DATA_CMS_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED_DATA_V0</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED_DATA_V2</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED_DATA_PKCS_1_5_VERSION</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED_DATA_CMS_VERSION</term>
	/// </item>
	/// </list>
	/// <para>Examples</para>
	/// <para>
	/// For an example that uses this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate
	/// Code for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program:
	/// Encoding and Decoding a Hashed Message.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsggetparam BOOL CryptMsgGetParam( HCRYPTMSG
	// hCryptMsg, DWORD dwParamType, DWORD dwIndex, void *pvData, DWORD *pcbData );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "5a05eb09-208f-4e94-abfa-c2f14c0a3164")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgGetParam(HCRYPTMSG hCryptMsg, CryptMsgParamType dwParamType, uint dwIndex,
		[Out, SizeDef(nameof(pcbData), SizingMethod.Query)] IntPtr pvData, ref uint pcbData);

	/// <summary>
	/// <para>
	/// The <c>CryptMsgOpenToDecode</c> function opens a cryptographic message for decoding and returns a handle of the opened message.
	/// The message remains open until the CryptMsgClose function is called.
	/// </para>
	/// <para>
	/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
	/// Internet Mail Extensions (S/MIME) email interoperability. For details, see the Remarks section of CryptMsgOpenToEncode.
	/// </para>
	/// </summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwFlags">
	/// <para>This parameter can be one of the following flags.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DETACHED_FLAG</term>
	/// <term>Indicates that the message to be decoded is detached. If this flag is not set, the message is not detached.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// If set, the hCryptProv passed to this function is released on the final CryptMsgUpdate. The handle is not released if the
	/// function fails.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwMsgType">
	/// <para>
	/// Specifies the type of message to decode. In most cases, the message type is determined from the message header and zero is
	/// passed for this parameter. In some cases, notably with Internet Explorer 3.0, messages do not have headers and the type of
	/// message to be decoded must be supplied in this function call. If the header is missing and zero is passed for this parameter,
	/// the function fails.
	/// </para>
	/// <para>This parameter can be one of the following predefined message types.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DATA</term>
	/// <term>The message is encoded data.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// <term>The message is an enveloped message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// <term>The message is a hashed message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// <term>The message is a signed message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// <term>The message is a signed and enveloped message.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="hCryptProv">
	/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Windows Server 2003 and Windows XP:</c> Specifies a handle for the cryptographic provider to use for hashing the message. For
	/// signed messages, hCryptProv is used for signature verification.This parameter's data type is <c>HCRYPTPROV</c>.
	/// </para>
	/// <para>
	/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, set this parameter to
	/// <c>NULL</c>. Passing in <c>NULL</c> causes the default RSA or DSS provider to be acquired before performing hash, signature
	/// verification, or recipient encryption operations.
	/// </para>
	/// </param>
	/// <param name="pRecipientInfo">This parameter is reserved for future use and must be <c>NULL</c>.</param>
	/// <param name="pStreamInfo">
	/// <para>When streaming is not being used, this parameter must be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Note</c> Streaming is not used with CMSG_HASHED messages. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// When streaming is being used, the pStreamInfo parameter is a pointer to a CMSG_STREAM_INFO structure that contains a pointer to
	/// a callback to be called when CryptMsgUpdate is executed or when CryptMsgControl is executed when decoding a streamed enveloped message.
	/// </para>
	/// <para>For a signed message, the callback is passed a block of the decoded bytes from the inner content of the message.</para>
	/// <para>
	/// For an enveloped message, after each call to CryptMsgUpdate, you must check to determine whether the
	/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available by calling the CryptMsgGetParam function. <c>CryptMsgGetParam</c> will fail
	/// and GetLastError will return CRYPT_E_STREAM_MSG_NOT_READY until <c>CryptMsgUpdate</c> has processed enough of the message to
	/// make the CMSG_ENVELOPE_ALGORITHM_PARAM property available. When the CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you can
	/// iterate through the recipients, retrieving a CERT_INFO structure for each recipient by using the <c>CryptMsgGetParam</c>
	/// function to retrieve the CMSG_RECIPIENT_INFO_PARAM property. To prevent a denial of service attack from an enveloped message
	/// that has an artificially large header block, you must keep track of the amount of memory that has been passed to the
	/// <c>CryptMsgUpdate</c> function during this process. If the amount of data exceeds an application defined limit before the
	/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you must stop processing the message and call the CryptMsgClose function to
	/// cause the operating system to release any memory that has been allocated for the message. A suggested limit is the maximum
	/// allowable size of a message. For example, if the maximum message size is 10 MB, the limit for this test should be 10 MB.
	/// </para>
	/// <para>
	/// The CERT_INFO structure is used to find a matching certificate in a previously opened certificate store by using the
	/// CertGetSubjectCertificateFromStore function. When the correct certificate is found, the CertGetCertificateContextProperty
	/// function with a CERT_KEY_PROV_INFO_PROP_ID parameter is called to retrieve a CRYPT_KEY_PROV_INFO structure. The structure
	/// contains the information necessary to acquire the recipient's private key by calling CryptAcquireContext, using the
	/// <c>pwszContainerName</c>, <c>pwszProvName</c>, <c>dwProvType</c>, and <c>dwFlags</c> members of the <c>CRYPT_KEY_PROV_INFO</c>
	/// structure. The <c>hCryptProv</c> acquired and the <c>dwKeySpec</c> member of the <c>CRYPT_KEY_PROV_INFO</c> structure are passed
	/// to the CryptMsgControl structure as a member of the CMSG_CTRL_DECRYPT_PARA structure to permit the start of the decryption of
	/// the inner content. The streaming code will then perform the decryption as the data is input. The resulting blocks of plaintext
	/// are passed to the callback function specified by the <c>pfnStreamOutput</c> member of the CMSG_STREAM_INFO structure to handle
	/// the output of the decrypted message.
	/// </para>
	/// <para>
	/// <c>Note</c> Streamed decoding of an enveloped message queues the ciphertext in memory until CryptMsgControl is called to start
	/// the decryption. The application must initiate decrypting in a timely manner so that the data can be saved to disk or routed
	/// elsewhere before the accumulated ciphertext becomes too large and the system runs out of memory.
	/// </para>
	/// <para>
	/// In the case of a signed message enclosed in an enveloped message, the plaintext output from the streaming decode of the
	/// enveloped message can be fed into another streaming decode to process the signed message.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns the handle of the opened message.</para>
	/// <para>If the function fails, it returns <c>NULL</c>. For extended error information, call GetLastError.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>A memory allocation failure occurred.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentodecode HCRYPTMSG CryptMsgOpenToDecode(
	// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV_LEGACY hCryptProv, PCERT_INFO pRecipientInfo,
	// PCMSG_STREAM_INFO pStreamInfo );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b3df6312-c866-4faa-8b89-bda67c697631")]
	public static extern SafeHCRYPTMSG CryptMsgOpenToDecode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType,
		[Optional] HCRYPTPROV hCryptProv, [Optional] IntPtr pRecipientInfo, in CMSG_STREAM_INFO pStreamInfo);

	/// <summary>
	/// <para>
	/// The <c>CryptMsgOpenToDecode</c> function opens a cryptographic message for decoding and returns a handle of the opened message.
	/// The message remains open until the CryptMsgClose function is called.
	/// </para>
	/// <para>
	/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
	/// Internet Mail Extensions (S/MIME) email interoperability. For details, see the Remarks section of CryptMsgOpenToEncode.
	/// </para>
	/// </summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwFlags">
	/// <para>This parameter can be one of the following flags.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DETACHED_FLAG</term>
	/// <term>Indicates that the message to be decoded is detached. If this flag is not set, the message is not detached.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// If set, the hCryptProv passed to this function is released on the final CryptMsgUpdate. The handle is not released if the
	/// function fails.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwMsgType">
	/// <para>
	/// Specifies the type of message to decode. In most cases, the message type is determined from the message header and zero is
	/// passed for this parameter. In some cases, notably with Internet Explorer 3.0, messages do not have headers and the type of
	/// message to be decoded must be supplied in this function call. If the header is missing and zero is passed for this parameter,
	/// the function fails.
	/// </para>
	/// <para>This parameter can be one of the following predefined message types.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DATA</term>
	/// <term>The message is encoded data.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// <term>The message is an enveloped message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// <term>The message is a hashed message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// <term>The message is a signed message.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// <term>The message is a signed and enveloped message.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="hCryptProv">
	/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Windows Server 2003 and Windows XP:</c> Specifies a handle for the cryptographic provider to use for hashing the message. For
	/// signed messages, hCryptProv is used for signature verification.This parameter's data type is <c>HCRYPTPROV</c>.
	/// </para>
	/// <para>
	/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, set this parameter to
	/// <c>NULL</c>. Passing in <c>NULL</c> causes the default RSA or DSS provider to be acquired before performing hash, signature
	/// verification, or recipient encryption operations.
	/// </para>
	/// </param>
	/// <param name="pRecipientInfo">This parameter is reserved for future use and must be <c>NULL</c>.</param>
	/// <param name="pStreamInfo">
	/// <para>When streaming is not being used, this parameter must be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Note</c> Streaming is not used with CMSG_HASHED messages. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// When streaming is being used, the pStreamInfo parameter is a pointer to a CMSG_STREAM_INFO structure that contains a pointer to
	/// a callback to be called when CryptMsgUpdate is executed or when CryptMsgControl is executed when decoding a streamed enveloped message.
	/// </para>
	/// <para>For a signed message, the callback is passed a block of the decoded bytes from the inner content of the message.</para>
	/// <para>
	/// For an enveloped message, after each call to CryptMsgUpdate, you must check to determine whether the
	/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available by calling the CryptMsgGetParam function. <c>CryptMsgGetParam</c> will fail
	/// and GetLastError will return CRYPT_E_STREAM_MSG_NOT_READY until <c>CryptMsgUpdate</c> has processed enough of the message to
	/// make the CMSG_ENVELOPE_ALGORITHM_PARAM property available. When the CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you can
	/// iterate through the recipients, retrieving a CERT_INFO structure for each recipient by using the <c>CryptMsgGetParam</c>
	/// function to retrieve the CMSG_RECIPIENT_INFO_PARAM property. To prevent a denial of service attack from an enveloped message
	/// that has an artificially large header block, you must keep track of the amount of memory that has been passed to the
	/// <c>CryptMsgUpdate</c> function during this process. If the amount of data exceeds an application defined limit before the
	/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you must stop processing the message and call the CryptMsgClose function to
	/// cause the operating system to release any memory that has been allocated for the message. A suggested limit is the maximum
	/// allowable size of a message. For example, if the maximum message size is 10 MB, the limit for this test should be 10 MB.
	/// </para>
	/// <para>
	/// The CERT_INFO structure is used to find a matching certificate in a previously opened certificate store by using the
	/// CertGetSubjectCertificateFromStore function. When the correct certificate is found, the CertGetCertificateContextProperty
	/// function with a CERT_KEY_PROV_INFO_PROP_ID parameter is called to retrieve a CRYPT_KEY_PROV_INFO structure. The structure
	/// contains the information necessary to acquire the recipient's private key by calling CryptAcquireContext, using the
	/// <c>pwszContainerName</c>, <c>pwszProvName</c>, <c>dwProvType</c>, and <c>dwFlags</c> members of the <c>CRYPT_KEY_PROV_INFO</c>
	/// structure. The <c>hCryptProv</c> acquired and the <c>dwKeySpec</c> member of the <c>CRYPT_KEY_PROV_INFO</c> structure are passed
	/// to the CryptMsgControl structure as a member of the CMSG_CTRL_DECRYPT_PARA structure to permit the start of the decryption of
	/// the inner content. The streaming code will then perform the decryption as the data is input. The resulting blocks of plaintext
	/// are passed to the callback function specified by the <c>pfnStreamOutput</c> member of the CMSG_STREAM_INFO structure to handle
	/// the output of the decrypted message.
	/// </para>
	/// <para>
	/// <c>Note</c> Streamed decoding of an enveloped message queues the ciphertext in memory until CryptMsgControl is called to start
	/// the decryption. The application must initiate decrypting in a timely manner so that the data can be saved to disk or routed
	/// elsewhere before the accumulated ciphertext becomes too large and the system runs out of memory.
	/// </para>
	/// <para>
	/// In the case of a signed message enclosed in an enveloped message, the plaintext output from the streaming decode of the
	/// enveloped message can be fed into another streaming decode to process the signed message.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns the handle of the opened message.</para>
	/// <para>If the function fails, it returns <c>NULL</c>. For extended error information, call GetLastError.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>A memory allocation failure occurred.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentodecode HCRYPTMSG CryptMsgOpenToDecode(
	// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV_LEGACY hCryptProv, PCERT_INFO pRecipientInfo,
	// PCMSG_STREAM_INFO pStreamInfo );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b3df6312-c866-4faa-8b89-bda67c697631")]
	public static extern SafeHCRYPTMSG CryptMsgOpenToDecode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType,
		HCRYPTPROV hCryptProv = default, IntPtr pRecipientInfo = default, ManagedStructPointer<CMSG_STREAM_INFO> pStreamInfo = default);

	/// <summary>
	/// The <c>CryptMsgOpenToEncode</c> function opens a cryptographic message for encoding and returns a handle of the opened message.
	/// The message remains open until CryptMsgClose is called.
	/// </summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwFlags">
	/// <para>Currently defined dwFlags are shown in the following table.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_BARE_CONTENT_FLAG</term>
	/// <term>
	/// The streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be streamed
	/// into an enclosing message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_DETACHED_FLAG</term>
	/// <term>There is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_AUTHENTICATED_ATTRIBUTES_FLAG</term>
	/// <term>
	/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
	/// otherwise be required.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
	/// <term>
	/// Used when calculating the size of a message that has been encoded by using Distinguished Encoding Rules (DER) and that is nested
	/// inside an enveloped message. This is particularly useful when performing streaming.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
	/// <term>
	/// When set, non-data type-inner content is encapsulated within an OCTET STRING. Applicable to both signed and enveloped messages.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released if
	/// the function fails.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwMsgType">
	/// <para>Indicates the message type. This must be one of the following values.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DATA</term>
	/// <term>This value is not used.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_SIGNED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_ENVELOPED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// <term>This value is not currently implemented.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_HASHED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvMsgEncodeInfo">
	/// The address of a structure that contains the encoding information. The type of data depends on the value of the dwMsgType
	/// parameter. For details, see dwMsgType.
	/// </param>
	/// <param name="pszInnerContentObjID">
	/// <para>
	/// If CryptMsgCalculateEncodedLength is called and the data for CryptMsgUpdate has already been message encoded, the appropriate
	/// object identifier (OID) is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, then the inner content type
	/// is assumed not to have been previously encoded and is therefore encoded as an octet string and given the type CMSG_DATA.
	/// </para>
	/// <para><c>Note</c> When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
	/// <para>
	/// The following algorithm OIDs are commonly used. A user can define new inner content usage by ensuring that the sender and
	/// receiver of the message agree upon the semantics associated with the OID.
	/// </para>
	/// <list type="bullet">
	/// <item>
	/// <term>szOID_RSA_data</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_envelopedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signEnvData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_digestedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_encryptedData</term>
	/// </item>
	/// <item>
	/// <term>SPC_INDIRECT_DATA_OBJID</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pStreamInfo">
	/// <para>
	/// When streaming is being used, this parameter is the address of a CMSG_STREAM_INFO structure. The callback function specified by
	/// the <c>pfnStreamOutput</c> member of the <c>CMSG_STREAM_INFO</c> structure is called when CryptMsgUpdate is executed. The
	/// callback is passed the encoded bytes that result from the encoding. For more information about how to use the callback, see <c>CMSG_STREAM_INFO</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When streaming is being used, the application must not release any data handles that are passed in the
	/// pvMsgEncodeInfo parameter, such as the provider handle in the <c>hCryptProv</c> member of the CMSG_SIGNER_ENCODE_INFO structure,
	/// until after the message handle returned by this function is closed by using the CryptMsgClose function.
	/// </para>
	/// <para>When streaming is not being used, this parameter is set to</para>
	/// <para>NULL</para>
	/// <para>.</para>
	/// <para>
	/// Streaming is not used with the <c>CMSG_HASHED</c> message type. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// Consider the case of a signed message being enclosed in an enveloped message. The encoded output from the streamed encoding of
	/// the signed message feeds into another streaming encoding of the enveloped message. The callback for the streaming encoding calls
	/// CryptMsgUpdate to encode the enveloped message. The callback for the enveloped message receives the encoded bytes of the nested
	/// signed message.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>
	/// If the function succeeds, it returns a handle to the opened message. This handle must be closed when it is no longer needed by
	/// passing it to the CryptMsgClose function.
	/// </para>
	/// <para>If this function fails, <c>NULL</c> is returned.</para>
	/// <para>To retrieve extended error information, use the GetLastError function.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The OID is badly formatted.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>There is not enough memory.</term>
	/// </item>
	/// </list>
	/// <para>In addition, if dwMsgType is CMSG_SIGNED, errors can be propagated from CryptCreateHash.</para>
	/// <para>If dwMsgType is CMSG_ENVELOPED, errors can be propagated from CryptGenKey, CryptImportKey, and CryptExportKey.</para>
	/// <para>If dwMsgType is CMSG_HASHED, errors can be propagated from CryptCreateHash.</para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// For functions that perform encryption, the encrypted symmetric keys are reversed from little-endian format to big-endian format
	/// after CryptExportKey is called internally. For functions that perform decryption, the encrypted symmetric keys are reversed from
	/// big-endian format to little-endian format before CryptImportKey is called.
	/// </para>
	/// <para>CRYPT_NO_SALT is specified when symmetric keys are generated and imported with CryptGenKey and CryptImportKey.</para>
	/// <para>
	/// Messages encrypted with the RC2 encryption algorithm use KP_EFFECTIVE_KEYLEN with CryptGetKeyParam to determine the effective
	/// key length of the RC2 key importing or exporting keys.
	/// </para>
	/// <para>
	/// For messages encrypted with the RC2 encryption algorithm, encode and decode operations have been updated to handle ASN RC2
	/// parameters for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
	/// </para>
	/// <para>
	/// For messages encrypted with the RC4, DES, and 3DES encryption algorithms, encode and decode operations now handle the ASN IV
	/// octet string parameter for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
	/// </para>
	/// <para>Examples</para>
	/// <para>
	/// For examples that use this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate Code
	/// for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program: Encoding and
	/// Decoding a Hashed Message.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentoencode HCRYPTMSG CryptMsgOpenToEncode(
	// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
	// PCMSG_STREAM_INFO pStreamInfo );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b0d2610b-05ba-4fb6-8f38-10f970a52091")]
	public static extern SafeHCRYPTMSG CryptMsgOpenToEncode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType,
		[In] IntPtr pvMsgEncodeInfo, [Optional] SafeOID pszInnerContentObjID, in CMSG_STREAM_INFO pStreamInfo);

	/// <summary>
	/// The <c>CryptMsgOpenToEncode</c> function opens a cryptographic message for encoding and returns a handle of the opened message.
	/// The message remains open until CryptMsgClose is called.
	/// </summary>
	/// <param name="dwMsgEncodingType">
	/// <para>
	/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
	/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
	/// <para>Currently defined encoding types are:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>X509_ASN_ENCODING</term>
	/// </item>
	/// <item>
	/// <term>PKCS_7_ASN_ENCODING</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwFlags">
	/// <para>Currently defined dwFlags are shown in the following table.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_BARE_CONTENT_FLAG</term>
	/// <term>
	/// The streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be streamed
	/// into an enclosing message.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_DETACHED_FLAG</term>
	/// <term>There is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_AUTHENTICATED_ATTRIBUTES_FLAG</term>
	/// <term>
	/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
	/// otherwise be required.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
	/// <term>
	/// Used when calculating the size of a message that has been encoded by using Distinguished Encoding Rules (DER) and that is nested
	/// inside an enveloped message. This is particularly useful when performing streaming.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
	/// <term>
	/// When set, non-data type-inner content is encapsulated within an OCTET STRING. Applicable to both signed and enveloped messages.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
	/// <term>
	/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released if
	/// the function fails.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="dwMsgType">
	/// <para>Indicates the message type. This must be one of the following values.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_DATA</term>
	/// <term>This value is not used.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_SIGNED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_ENVELOPED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_ENVELOPED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
	/// <term>This value is not currently implemented.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_HASHED</term>
	/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_HASHED_ENCODE_INFO structure that contains the encoding information.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvMsgEncodeInfo">
	/// The address of a structure that contains the encoding information. The type of data depends on the value of the dwMsgType
	/// parameter. For details, see dwMsgType.
	/// </param>
	/// <param name="pszInnerContentObjID">
	/// <para>
	/// If CryptMsgCalculateEncodedLength is called and the data for CryptMsgUpdate has already been message encoded, the appropriate
	/// object identifier (OID) is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, then the inner content type
	/// is assumed not to have been previously encoded and is therefore encoded as an octet string and given the type CMSG_DATA.
	/// </para>
	/// <para><c>Note</c> When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
	/// <para>
	/// The following algorithm OIDs are commonly used. A user can define new inner content usage by ensuring that the sender and
	/// receiver of the message agree upon the semantics associated with the OID.
	/// </para>
	/// <list type="bullet">
	/// <item>
	/// <term>szOID_RSA_data</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_envelopedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_signEnvData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_digestedData</term>
	/// </item>
	/// <item>
	/// <term>szOID_RSA_encryptedData</term>
	/// </item>
	/// <item>
	/// <term>SPC_INDIRECT_DATA_OBJID</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pStreamInfo">
	/// <para>
	/// When streaming is being used, this parameter is the address of a CMSG_STREAM_INFO structure. The callback function specified by
	/// the <c>pfnStreamOutput</c> member of the <c>CMSG_STREAM_INFO</c> structure is called when CryptMsgUpdate is executed. The
	/// callback is passed the encoded bytes that result from the encoding. For more information about how to use the callback, see <c>CMSG_STREAM_INFO</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When streaming is being used, the application must not release any data handles that are passed in the
	/// pvMsgEncodeInfo parameter, such as the provider handle in the <c>hCryptProv</c> member of the CMSG_SIGNER_ENCODE_INFO structure,
	/// until after the message handle returned by this function is closed by using the CryptMsgClose function.
	/// </para>
	/// <para>When streaming is not being used, this parameter is set to</para>
	/// <para>NULL</para>
	/// <para>.</para>
	/// <para>
	/// Streaming is not used with the <c>CMSG_HASHED</c> message type. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
	/// </para>
	/// <para>
	/// Consider the case of a signed message being enclosed in an enveloped message. The encoded output from the streamed encoding of
	/// the signed message feeds into another streaming encoding of the enveloped message. The callback for the streaming encoding calls
	/// CryptMsgUpdate to encode the enveloped message. The callback for the enveloped message receives the encoded bytes of the nested
	/// signed message.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>
	/// If the function succeeds, it returns a handle to the opened message. This handle must be closed when it is no longer needed by
	/// passing it to the CryptMsgClose function.
	/// </para>
	/// <para>If this function fails, <c>NULL</c> is returned.</para>
	/// <para>To retrieve extended error information, use the GetLastError function.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The OID is badly formatted.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>There is not enough memory.</term>
	/// </item>
	/// </list>
	/// <para>In addition, if dwMsgType is CMSG_SIGNED, errors can be propagated from CryptCreateHash.</para>
	/// <para>If dwMsgType is CMSG_ENVELOPED, errors can be propagated from CryptGenKey, CryptImportKey, and CryptExportKey.</para>
	/// <para>If dwMsgType is CMSG_HASHED, errors can be propagated from CryptCreateHash.</para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// For functions that perform encryption, the encrypted symmetric keys are reversed from little-endian format to big-endian format
	/// after CryptExportKey is called internally. For functions that perform decryption, the encrypted symmetric keys are reversed from
	/// big-endian format to little-endian format before CryptImportKey is called.
	/// </para>
	/// <para>CRYPT_NO_SALT is specified when symmetric keys are generated and imported with CryptGenKey and CryptImportKey.</para>
	/// <para>
	/// Messages encrypted with the RC2 encryption algorithm use KP_EFFECTIVE_KEYLEN with CryptGetKeyParam to determine the effective
	/// key length of the RC2 key importing or exporting keys.
	/// </para>
	/// <para>
	/// For messages encrypted with the RC2 encryption algorithm, encode and decode operations have been updated to handle ASN RC2
	/// parameters for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
	/// </para>
	/// <para>
	/// For messages encrypted with the RC4, DES, and 3DES encryption algorithms, encode and decode operations now handle the ASN IV
	/// octet string parameter for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
	/// </para>
	/// <para>Examples</para>
	/// <para>
	/// For examples that use this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate Code
	/// for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program: Encoding and
	/// Decoding a Hashed Message.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentoencode HCRYPTMSG CryptMsgOpenToEncode(
	// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
	// PCMSG_STREAM_INFO pStreamInfo );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b0d2610b-05ba-4fb6-8f38-10f970a52091")]
	public static extern SafeHCRYPTMSG CryptMsgOpenToEncode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType,
		[In] IntPtr pvMsgEncodeInfo, [Optional] SafeOID pszInnerContentObjID, IntPtr pStreamInfo = default);

	/// <summary>
	/// The <c>CryptMsgUpdate</c> function adds contents to a cryptographic message. The use of this function allows messages to be
	/// constructed piece by piece through repetitive calls of <c>CryptMsgUpdate</c>. The added message content is either encoded or
	/// decoded depending on whether the message was opened with CryptMsgOpenToEncode or CryptMsgOpenToDecode.
	/// </summary>
	/// <param name="hCryptMsg">Cryptographic message handle of the message to be updated.</param>
	/// <param name="pbData">A pointer to the buffer holding the data to be encoded or decoded.</param>
	/// <param name="cbData">Number of bytes of data in the pbData buffer.</param>
	/// <param name="fFinal">
	/// <para>
	/// Indicates that the last block of data for encoding or decoding is being processed. Correct usage of this flag is dependent upon
	/// whether the message being processed has detached data. The inclusion of detached data in a message is indicated by setting
	/// dwFlags to CMSG_DETACHED_FLAG in the call to the function that opened the message.
	/// </para>
	/// <para>
	/// If CMSG_DETACHED_FLAG was not set and the message was opened using either CryptMsgOpenToDecode or CryptMsgOpenToEncode, fFinal
	/// is set to <c>TRUE</c>, and <c>CryptMsgUpdate</c> is only called once.
	/// </para>
	/// <para>
	/// If the CMSG_DETACHED_FLAG flag was set and a message is opened using CryptMsgOpenToEncode, fFinal is set to <c>TRUE</c> only on
	/// the last call to <c>CryptMsgUpdate</c>.
	/// </para>
	/// <para>
	/// If the CMSG_DETACHED_FLAG flag was set and a message is opened using CryptMsgOpenToDecode, fFinal is set to <c>TRUE</c> when the
	/// header is processed by a single call to <c>CryptMsgUpdate</c>. It is set to <c>FALSE</c> while processing the detached data in
	/// subsequent calls to <c>CryptMsgUpdate</c> until the last detached data block is to be processed. On the last call to
	/// <c>CryptMsgUpdate</c>, it is set to <c>TRUE</c>.
	/// </para>
	/// <para>
	/// When detached data is decoded, the header and the content of a message are contained in different BLOBs. Each BLOB requires that
	/// fFinal be set to <c>TRUE</c> when the last call to the function is made for that BLOB.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>
	/// Errors encountered in the application defined callback function specified by pStreamInfo in CryptMsgOpenToDecode and
	/// CryptMsgOpenToEncode might be propagated to <c>CryptMsgUpdate</c> if streaming is used. If this happens, SetLastError is not
	/// called by <c>CryptMsgUpdate</c> after the callback function returns, which preserves any errors encountered under the control of
	/// the application. It is the responsibility of the callback function (or one of the APIs that it calls) to call
	/// <c>SetLastError</c> if an error occurs while the application is processing the streamed data.
	/// </para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
	/// <term>The message type is not valid.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_MSG_ERROR</term>
	/// <term>An error was encountered doing a cryptographic operation.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_OID_FORMAT</term>
	/// <term>The object identifier is badly formatted.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Ran out of memory.</term>
	/// </item>
	/// </list>
	/// <para>Propagated errors might be encountered from any of the following functions:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptSignHash</term>
	/// </item>
	/// <item>
	/// <term>CryptGetKeyParam</term>
	/// </item>
	/// <item>
	/// <term>CryptEncrypt</term>
	/// </item>
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgupdate BOOL CryptMsgUpdate( HCRYPTMSG hCryptMsg,
	// const BYTE *pbData, DWORD cbData, BOOL fFinal );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d27d75f0-1646-4926-b375-59e52b00326c")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgUpdate(HCRYPTMSG hCryptMsg, [In, SizeDef(nameof(cbData))] IntPtr pbData, uint cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal);

	/// <summary>
	/// The <c>CryptMsgVerifyCountersignatureEncoded</c> function verifies a countersignature in terms of the SignerInfo structure (as
	/// defined by PKCS #7).
	/// </summary>
	/// <param name="hCryptProv">
	/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Windows Server 2003 and Windows XP:</c><c>NULL</c> or the handle of the cryptographic provider to use to hash the
	/// encryptedDigest field of pbSignerInfo.This parameter's data type is <c>HCRYPTPROV</c>.
	/// </para>
	/// <para>
	/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, pass <c>NULL</c> to cause the
	/// default RSA or DSS provider to be used.
	/// </para>
	/// </param>
	/// <param name="dwEncodingType">
	/// <para>
	/// Specifies the encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional
	/// encoding types may be added in the future. For either current encoding type, use:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
	/// </param>
	/// <param name="pbSignerInfo">A pointer to the encoded BLOB that contains the signer of the contents of a message to be countersigned.</param>
	/// <param name="cbSignerInfo">Count, in bytes, of the encoded BLOB for the signer of the contents.</param>
	/// <param name="pbSignerInfoCountersignature">A pointer to the encoded BLOB containing the countersigner information.</param>
	/// <param name="cbSignerInfoCountersignature">Count, in bytes, of the encoded BLOB for the countersigner of the message.</param>
	/// <param name="pciCountersigner">
	/// A pointer to a CERT_INFO that includes with the issuer and serial number of the countersigner. For more information, see Remarks.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
	/// <term>The message does not contain an expected authenticated attribute.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_HASH_VALUE</term>
	/// <term>The hash value is not correct.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Ran out of memory.</term>
	/// </item>
	/// </list>
	/// <para>Propagated errors from the following functions might be returned.</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptImportKey</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifySignature</term>
	/// </item>
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// Countersigner verification is done using the PKCS #7 <c>SIGNERINFO</c> structure. The signature must contain the encrypted hash
	/// of the encryptedDigest field of pbSignerInfo.
	/// </para>
	/// <para>
	/// The issuer and serial number of the countersigner must match the countersigner information from pbSignerInfoCountersignature.
	/// The only fields referenced from pciCountersigner are SerialNumber, Issuer, and SubjectPublicKeyInfo. The SubjectPublicKeyInfo is
	/// used to access the public key that is then used to encrypt the hash from the pciCountersigner so compare it with the hash from
	/// the pbSignerInfo.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a CounterSigned Message.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgverifycountersignatureencoded BOOL
	// CryptMsgVerifyCountersignatureEncoded( HCRYPTPROV_LEGACY hCryptProv, DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD
	// cbSignerInfo, PBYTE pbSignerInfoCountersignature, DWORD cbSignerInfoCountersignature, PCERT_INFO pciCountersigner );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b0332360-a737-4b48-b592-0c55d493a02d")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgVerifyCountersignatureEncoded([Optional] HCRYPTPROV hCryptProv, CertEncodingType dwEncodingType,
		[In, SizeDef(nameof(cbSignerInfo))] IntPtr pbSignerInfo, uint cbSignerInfo,
		[In, SizeDef(nameof(cbSignerInfoCountersignature))] IntPtr pbSignerInfoCountersignature, uint cbSignerInfoCountersignature, in CERT_INFO pciCountersigner);

	/// <summary>
	/// The <c>CryptMsgVerifyCountersignatureEncodedEx</c> function verifies that the pbSignerInfoCounterSignature parameter contains
	/// the encrypted hash of the <c>encryptedDigest</c> field of the pbSignerInfo parameter structure. The signer can be a
	/// CERT_PUBLIC_KEY_INFO structure, a certificate context, or a chain context.
	/// </summary>
	/// <param name="hCryptProv">
	/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
	/// <para>
	/// <c>Windows Server 2003 and Windows XP:</c><c>NULL</c> or the handle of the cryptographic provider to use to hash the
	/// encryptedDigest field of pbSignerInfo.This parameter's data type is <c>HCRYPTPROV</c>.
	/// </para>
	/// <para>
	/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, pass <c>NULL</c> to cause the
	/// default RSA or DSS provider to be used.
	/// </para>
	/// </param>
	/// <param name="dwEncodingType">
	/// <para>
	/// The encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional encoding
	/// types may be added in the future. For either current encoding type, use:
	/// </para>
	/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
	/// </param>
	/// <param name="pbSignerInfo">A pointer to the encoded BLOB that contains the signer of the contents of a message to be countersigned.</param>
	/// <param name="cbSignerInfo">The count, in bytes, of the encoded BLOB for the signer of the contents.</param>
	/// <param name="pbSignerInfoCountersignature">A pointer to the encoded BLOB containing the countersigner information.</param>
	/// <param name="cbSignerInfoCountersignature">The count, in bytes, of the encoded BLOB for the countersigner of the message.</param>
	/// <param name="dwSignerType">
	/// <para>
	/// The structure that contains the signer information. The following table shows the predefined values and the structures indicated.
	/// </para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_VERIFY_SIGNER_PUBKEY</term>
	/// <term>pvSigner is a pointer to a CERT_PUBLIC_KEY_INFO structure.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_VERIFY_SIGNER_CERT</term>
	/// <term>pvSigner is a pointer to a CERT_CONTEXT structure.</term>
	/// </item>
	/// <item>
	/// <term>CMSG_VERIFY_SIGNER_CHAIN</term>
	/// <term>pvSigner is a pointer to a CERT_CHAIN_CONTEXT structure.</term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvSigner">
	/// A pointer to a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a chain context depending on the value of dwSignerType.
	/// </param>
	/// <param name="dwFlags">
	/// <para>Flags that modify the function behavior. This can be zero or the following value.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Value</term>
	/// <term>Meaning</term>
	/// </listheader>
	/// <item>
	/// <term>CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG 0x00000001</term>
	/// <term>
	/// Performs a strong signature check after successful signature verification. Set the pvExtra parameter to point to a
	/// CERT_STRONG_SIGN_PARA structure that contains the parameters needed to check the signature strength.. Windows 8 and Windows
	/// Server 2012: Support for this flag begins.
	/// </term>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="pvExtra">
	/// If you set the dwFlags parameter to <c>CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG</c>, set this parameter (pvExtra) to point to
	/// a CERT_STRONG_SIGN_PARA structure that contains the parameters used to check the signature strength.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
	/// <para>The following error codes are most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
	/// <term>The message does not contain an expected authenticated attribute.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_HASH_VALUE</term>
	/// <term>The hash value is not correct.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
	/// <term>The message is not encoded as expected.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNKNOWN_ALGO</term>
	/// <term>The cryptographic algorithm is unknown.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>One or more arguments are not valid.</term>
	/// </item>
	/// <item>
	/// <term>E_OUTOFMEMORY</term>
	/// <term>Ran out of memory.</term>
	/// </item>
	/// </list>
	/// <para>Propagated errors from the following functions might be returned.</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptHashData</term>
	/// </item>
	/// <item>
	/// <term>CryptGetHashParam</term>
	/// </item>
	/// <item>
	/// <term>CryptImportKey</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifySignature</term>
	/// </item>
	/// <item>
	/// <term>CryptCreateHash</term>
	/// </item>
	/// </list>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// Countersigner verification is done using the PKCS #7 <c>SIGNERINFO</c> structure. The signature must contain the encrypted hash
	/// of the encryptedDigest field of pbSignerInfo.
	/// </para>
	/// <para>
	/// The issuer and serial number of the countersigner must match the countersigner information from pbSignerInfoCountersignature.
	/// The only fields referenced from pciCountersigner are SerialNumber, Issuer, and SubjectPublicKeyInfo. The SubjectPublicKeyInfo is
	/// used to access the public key that is then used to encrypt the hash from the pciCountersigner so compare it with the hash from
	/// the pbSignerInfo.
	/// </para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a CounterSigned Message.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgverifycountersignatureencodedex BOOL
	// CryptMsgVerifyCountersignatureEncodedEx( HCRYPTPROV_LEGACY hCryptProv, DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD
	// cbSignerInfo, PBYTE pbSignerInfoCountersignature, DWORD cbSignerInfoCountersignature, DWORD dwSignerType, void *pvSigner, DWORD
	// dwFlags, void *pvExtra );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "da756cd5-1dec-4d88-9c90-76dd263035eb")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptMsgVerifyCountersignatureEncodedEx([Optional] HCRYPTPROV hCryptProv, CertEncodingType dwEncodingType,
		[In, SizeDef(nameof(cbSignerInfo))] IntPtr pbSignerInfo, uint cbSignerInfo,
		[In, SizeDef(nameof(cbSignerInfoCountersignature))] IntPtr pbSignerInfoCountersignature, uint cbSignerInfoCountersignature,
		CryptMsgSignerType dwSignerType, IntPtr pvSigner, CryptMsgVerifyCounterFlags dwFlags, [Optional] IntPtr pvExtra);

	/// <summary>
	/// The <c>CryptSignAndEncryptMessage</c> function creates a hash of the specified content, signs the hash, encrypts the content,
	/// hashes the encrypted contents and the signed hash, and then encodes both the encrypted content and the signed hash. The result
	/// is the same as if the hash were first signed and then encrypted.
	/// </summary>
	/// <param name="pSignPara">A pointer to a CRYPT_SIGN_MESSAGE_PARA structure that contains the signature parameters.</param>
	/// <param name="pEncryptPara">A pointer to a CRYPT_ENCRYPT_MESSAGE_PARA structure containing encryption parameters.</param>
	/// <param name="cRecipientCert">Number of array elements in rgpRecipientCert.</param>
	/// <param name="rgpRecipientCert">
	/// Array of pointers to CERT_CONTEXT structures. Each structure is the certificate of an intended recipients of the message.
	/// </param>
	/// <param name="pbToBeSignedAndEncrypted">A pointer to a buffer containing the content to be signed and encrypted.</param>
	/// <param name="cbToBeSignedAndEncrypted">The size, in bytes, of the pbToBeSignedAndEncrypted buffer.</param>
	/// <param name="pbSignedAndEncryptedBlob">
	/// <para>A pointer to a buffer to receive the encrypted and encoded message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbSignedAndEncryptedBlob">
	/// <para>
	/// A pointer to <c>DWORD</c> specifying the size, in bytes, of the buffer pointed to by pbSignedAndEncryptedBlob. When the function
	/// returns, this variable contains the size, in bytes, of the signed and encrypted message copied to *pbSignedAndEncryptedBlob.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero (FALSE).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error code most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbSignedAndEncryptedBlob parameter is not large enough to hold the returned data, the function
	/// sets the ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedAndEncryptedBlob.
	/// </term>
	/// </item>
	/// </list>
	/// <para><c>Note</c> Errors from the called functions CryptSignMessage and CryptEncryptMessage might be propagated to this function.</para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignandencryptmessage BOOL
	// CryptSignAndEncryptMessage( PCRYPT_SIGN_MESSAGE_PARA pSignPara, PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, DWORD cRecipientCert,
	// PCCERT_CONTEXT [] rgpRecipientCert, const BYTE *pbToBeSignedAndEncrypted, DWORD cbToBeSignedAndEncrypted, BYTE
	// *pbSignedAndEncryptedBlob, DWORD *pcbSignedAndEncryptedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "0ab234f2-a681-463f-8ba8-b23b05cf2626")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptSignAndEncryptMessage(in CRYPT_SIGN_MESSAGE_PARA pSignPara, in CRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, uint cRecipientCert,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] PCCERT_CONTEXT[] rgpRecipientCert,
		[In, SizeDef(nameof(cbToBeSignedAndEncrypted))] IntPtr pbToBeSignedAndEncrypted, uint cbToBeSignedAndEncrypted,
		[Out, SizeDef(nameof(pcbSignedAndEncryptedBlob), SizingMethod.Query)] IntPtr pbSignedAndEncryptedBlob, ref uint pcbSignedAndEncryptedBlob);

	/// <summary>
	/// The <c>CryptSignMessage</c> function creates a hash of the specified content, signs the hash, and then encodes both the original
	/// message content and the signed hash.
	/// </summary>
	/// <param name="pSignPara">A pointer to CRYPT_SIGN_MESSAGE_PARA structure containing the signature parameters.</param>
	/// <param name="fDetachedSignature">
	/// <c>TRUE</c> if this is to be a detached signature. Otherwise, <c>FALSE</c>. If this parameter is set to <c>TRUE</c>, only the
	/// signed hash is encoded in pbSignedBlob. Otherwise, both rgpbToBeSigned and the signed hash are encoded.
	/// </param>
	/// <param name="cToBeSigned">
	/// Count of the number of array elements in rgpbToBeSigned and rgpbToBeSigned. This parameter must be set to one unless
	/// fDetachedSignature is set to <c>TRUE</c>.
	/// </param>
	/// <param name="rgpbToBeSigned">Array of pointers to buffers that contain the contents to be signed.</param>
	/// <param name="rgcbToBeSigned">Array of sizes, in bytes, of the content buffers pointed to in rgpbToBeSigned.</param>
	/// <param name="pbSignedBlob">
	/// <para>
	/// A pointer to a buffer to receive the encoded signed hash, if fDetachedSignature is <c>TRUE</c>, or to both the encoded content
	/// and signed hash if fDetachedSignature is <c>FALSE</c>.
	/// </para>
	/// <para>
	/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbSignedBlob">
	/// <para>
	/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbSignedBlob buffer. When the function returns, this variable
	/// contains the size, in bytes, of the signed and encoded message.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbSignedBlob parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedBlob.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pSignPara is not valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_KEY_PROPERTY</term>
	/// <term>The pSigningCert in *pSignPara does not have a CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.</term>
	/// </item>
	/// </list>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptSignHash might be propagated to this function.
	/// </para>
	/// <para>
	/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
	/// about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignmessage BOOL CryptSignMessage(
	// PCRYPT_SIGN_MESSAGE_PARA pSignPara, BOOL fDetachedSignature, DWORD cToBeSigned, const BYTE * [] rgpbToBeSigned, DWORD []
	// rgcbToBeSigned, BYTE *pbSignedBlob, DWORD *pcbSignedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "f14f7c7b-14ac-40a7-9a49-d1a899ecc52a")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptSignMessage(in CRYPT_SIGN_MESSAGE_PARA pSignPara, [MarshalAs(UnmanagedType.Bool)] bool fDetachedSignature, uint cToBeSigned,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] rgpbToBeSigned,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] uint[] rgcbToBeSigned,
		[Out, SizeDef(nameof(pcbSignedBlob), SizingMethod.Query)] IntPtr pbSignedBlob, ref uint pcbSignedBlob);

	/// <summary>
	/// The <c>CryptSignMessageWithKey</c> function signs a message by using a CSP's private key specified in the parameters. A
	/// placeholder <c>SignerId</c> is created and stored in the message.
	/// </summary>
	/// <param name="pSignPara">A pointer to a CRYPT_KEY_SIGN_MESSAGE_PARA structure that contains the signature parameters.</param>
	/// <param name="pbToBeSigned">A pointer to a buffer array that contains the message to be signed.</param>
	/// <param name="cbToBeSigned">The number of array elements in the pbToBeSigned buffer array.</param>
	/// <param name="pbSignedBlob">
	/// <para>A pointer to a buffer to receive the encoded signed message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbSignedBlob">
	/// <para>
	/// A pointer to a <c>DWORD</c> value that indicates the size, in bytes, of the pbSignedBlob buffer. When the function returns, this
	/// variable contains the size, in bytes, of the signed and encoded message.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero (FALSE).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbSignedBlob parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedBlob.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pSignPara is not valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_KEY_PROPERTY</term>
	/// <term>The pSigningCert in *pSignPara does not have a CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignmessagewithkey BOOL CryptSignMessageWithKey(
	// PCRYPT_KEY_SIGN_MESSAGE_PARA pSignPara, const BYTE *pbToBeSigned, DWORD cbToBeSigned, BYTE *pbSignedBlob, DWORD *pcbSignedBlob );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d31024bf-7022-440b-8134-a02578510357")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptSignMessageWithKey(in CRYPT_KEY_SIGN_MESSAGE_PARA pSignPara, [In, SizeDef(nameof(cbToBeSigned))] IntPtr pbToBeSigned,
		uint cbToBeSigned, [Out, SizeDef(nameof(pcbSignedBlob), SizingMethod.Query)] IntPtr pbSignedBlob, ref uint pcbSignedBlob);

	/// <summary>The <c>CryptVerifyDetachedMessageHash</c> function verifies a detached hash.</summary>
	/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure containing the hash parameters.</param>
	/// <param name="pbDetachedHashBlob">A pointer to the encoded, detached hash.</param>
	/// <param name="cbDetachedHashBlob">The size, in bytes, of the detached hash.</param>
	/// <param name="cToBeHashed">Number of elements in the rgpbToBeHashed and rgcbToBeHashed arrays.</param>
	/// <param name="rgpbToBeHashed">Array of pointers to content buffers to be hashed.</param>
	/// <param name="rgcbToBeHashed">
	/// Array of sizes, in bytes, for the content buffers pointed to by the elements of the rgcbToBeHashed array.
	/// </param>
	/// <param name="pbComputedHash">
	/// <para>A pointer to a buffer to receive the computed hash.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the newly created hash is not needed for additional processing, or to set the size of the
	/// hash for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbComputedHash">
	/// <para>
	/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbComputedHash buffer. When the function returns, this
	/// <c>DWORD</c> contains the size, in bytes, of the created hash. The hash will not be returned if this parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned , applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero (FALSE).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a hashed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbComputedHash parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbComputedHash.
	/// </term>
	/// </item>
	/// </list>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this
	/// function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For
	/// information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifydetachedmessagehash BOOL
	// CryptVerifyDetachedMessageHash( PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbDetachedHashBlob, DWORD cbDetachedHashBlob, DWORD
	// cToBeHashed, const BYTE * [] rgpbToBeHashed, DWORD [] rgcbToBeHashed, BYTE *pbComputedHash, DWORD *pcbComputedHash );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "b529b9e2-9798-4548-a44f-c330524a3e6b")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyDetachedMessageHash(in CRYPT_HASH_MESSAGE_PARA pHashPara,
		[In, SizeDef(nameof(cbDetachedHashBlob))] IntPtr pbDetachedHashBlob, uint cbDetachedHashBlob,
		uint cToBeHashed, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] IntPtr[] rgpbToBeHashed,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] uint[] rgcbToBeHashed,
		[Out, SizeDef(nameof(pcbComputedHash), SizingMethod.Query)] IntPtr pbComputedHash, ref uint pcbComputedHash);

	/// <summary>
	/// The <c>CryptVerifyDetachedMessageSignature</c> function verifies a signed message containing a detached signature or signatures.
	/// </summary>
	/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure containing the verification parameters.</param>
	/// <param name="dwSignerIndex">
	/// Index of the signature to be verified. A message might have several signers and this function can be called repeatedly, changing
	/// dwSignerIndex to verify other signatures. If the function returns FALSE, and GetLastError returns CRYPT_E_NO_SIGNER, the
	/// previous call received the last signer of the message.
	/// </param>
	/// <param name="pbDetachedSignBlob">A pointer to a BLOB containing the encoded message signatures.</param>
	/// <param name="cbDetachedSignBlob">The size, in bytes, of the detached signature.</param>
	/// <param name="cToBeSigned">Number of array elements in rgpbToBeSigned and rgcbToBeSigned.</param>
	/// <param name="rgpbToBeSigned">Array of pointers to buffers containing the contents to be hashed.</param>
	/// <param name="rgcbToBeSigned">Array of sizes, in bytes, for the content buffers pointed to in rgpbToBeSigned.</param>
	/// <param name="ppSignerCert">
	/// A pointer to a pointer to a CERT_CONTEXT structure of a signer certificate. When you have finished using the certificate
	/// context, free it by calling the CertFreeCertificateContext function. A pointer to a <c>CERT_CONTEXT</c> structure will not be
	/// returned if this parameter is <c>NULL</c>.
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero ( <c>FALSE</c>).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pVerifyPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a signed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_SIGNER</term>
	/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_SIGNATURE</term>
	/// <term>The message's signature was not verified.</term>
	/// </item>
	/// </list>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, CryptVerifySignature, and CryptImportKey might be
	/// propagated to this function.If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1)
	/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifydetachedmessagesignature BOOL
	// CryptVerifyDetachedMessageSignature( PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE *pbDetachedSignBlob,
	// DWORD cbDetachedSignBlob, DWORD cToBeSigned, const BYTE * [] rgpbToBeSigned, DWORD [] rgcbToBeSigned, PCCERT_CONTEXT
	// *ppSignerCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "d437f6bf-eb56-4d29-bb91-eb8487e50219")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyDetachedMessageSignature(in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara, uint dwSignerIndex,
		[In, SizeDef(nameof(cbDetachedSignBlob))] IntPtr pbDetachedSignBlob, uint cbDetachedSignBlob,
		uint cToBeSigned, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] IntPtr[] rgpbToBeSigned,
		[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] uint[] rgcbToBeSigned, out SafePCCERT_CONTEXT ppSignerCert);

	/// <summary>The <c>CryptVerifyMessageHash</c> function verifies the hash of specified content.</summary>
	/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure containing hash parameters.</param>
	/// <param name="pbHashedBlob">A pointer to a buffer containing original content and its hash.</param>
	/// <param name="cbHashedBlob">The size, in bytes, of the original hash buffer.</param>
	/// <param name="pbToBeHashed">
	/// <para>A pointer to a buffer to receive the original content that was hashed.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the original content is not needed for additional processing, or to set the size of the
	/// original content for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbToBeHashed">
	/// <para>
	/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbToBeHashed buffer. When the function returns, this variable
	/// contains the size, in bytes, of the original content copied to pbToBeHashed. The original content will not be returned if this
	/// parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="pbComputedHash">
	/// A pointer to a buffer to receive the computed hash. This parameter can be <c>NULL</c> if the created hash is not needed for
	/// additional processing, or to set the size of the original content for memory allocation purposes. For more information, see
	/// Retrieving Data of Unknown Length.
	/// </param>
	/// <param name="pcbComputedHash">
	/// <para>
	/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbComputedHash buffer. When the function returns, this
	/// variable contains the size, in bytes, of the created hash. The hash is not returned if this parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
	/// <para>If the function fails, the return value is zero (FALSE).</para>
	/// <para>For extended error information, call GetLastError.</para>
	/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a hashed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
	/// </term>
	/// </item>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbToBeHashed parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbToBeHashed.
	/// </term>
	/// </item>
	/// </list>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this
	/// function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For
	/// information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagehash BOOL CryptVerifyMessageHash(
	// PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbHashedBlob, DWORD cbHashedBlob, BYTE *pbToBeHashed, DWORD *pcbToBeHashed, BYTE
	// *pbComputedHash, DWORD *pcbComputedHash );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "3b5185b9-e24b-4302-a60c-74ccbd19077c")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyMessageHash(in CRYPT_HASH_MESSAGE_PARA pHashPara, [In, SizeDef(nameof(cbHashedBlob))] IntPtr pbHashedBlob,
		uint cbHashedBlob, [Out, SizeDef(nameof(pcbToBeHashed), SizingMethod.Query)] IntPtr pbToBeHashed,
		ref uint pcbToBeHashed, [Out, SizeDef(nameof(pcbComputedHash), SizingMethod.Query)] IntPtr pbComputedHash, ref uint pcbComputedHash);

	/// <summary>
	/// <para>The <c>CryptVerifyMessageSignature</c> function verifies a signed message's signature.</para>
	/// <para>
	/// This function should not be used to verify the signature of a detached message. You should use the
	/// CryptVerifyDetachedMessageSignature function to verify the signature of a detached message.
	/// </para>
	/// </summary>
	/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
	/// <param name="dwSignerIndex">
	/// The index of the desired signature. There can be more than one signature. <c>CryptVerifyMessageSignature</c> can be called
	/// repeatedly, incrementing dwSignerIndex each time. Set this parameter to zero for the first signer, or if there is only one
	/// signer. If the function returns <c>FALSE</c>, and GetLastError returns CRYPT_E_NO_SIGNER, the previous call processed the last
	/// signer of the message.
	/// </param>
	/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
	/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
	/// <param name="pbDecoded">
	/// <para>A pointer to a buffer to receive the decoded message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
	/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecoded">
	/// <para>
	/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
	/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <param name="ppSignerCert">
	/// The address of a CERT_CONTEXT structure pointer that receives the certificate of the signer. When you have finished using this
	/// structure, free it by passing this pointer to the CertFreeCertificateContext function. This parameter can be <c>NULL</c> if the
	/// signer's certificate is not needed.
	/// </param>
	/// <returns>
	/// <para>
	/// If the function succeeds, the function returns nonzero. This does not necessarily mean that the signature was verified. In the
	/// case of a detached message, the variable pointed to by pcbDecoded will contain zero. In this case, this function will return
	/// nonzero, but the signature is not verified. To verify the signature of a detached message, use the
	/// CryptVerifyDetachedMessageSignature function.
	/// </para>
	/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
	/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pVerifyPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a signed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_SIGNER</term>
	/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_SIGNATURE</term>
	/// <term>The message's signature was not verified.</term>
	/// </item>
	/// </list>
	/// <para>
	/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, CryptVerifySignature, and CryptImportKey can be
	/// propagated to this function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1)
	/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
	/// </para>
	/// </returns>
	/// <remarks>
	/// <para>
	/// For a verified signer and message, ppSignerCert is updated with the CERT_CONTEXT of the signer. It must be freed by calling
	/// CertFreeCertificateContext. Otherwise, ppSignerCert is set to <c>NULL</c>.
	/// </para>
	/// <para>For a message that contains only certificates and CRLs, pcbDecoded is set to <c>NULL</c>.</para>
	/// <para>Examples</para>
	/// <para>For an example that uses this function, see Example C Program: Signing a Message and Verifying a Message Signature.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignature BOOL
	// CryptVerifyMessageSignature( PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE *pbSignedBlob, DWORD
	// cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded, PCCERT_CONTEXT *ppSignerCert );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "03411e7a-b097-4059-a198-3d412ae40e38")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyMessageSignature(in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara, uint dwSignerIndex,
		[In, SizeDef(nameof(cbSignedBlob))] IntPtr pbSignedBlob, uint cbSignedBlob,
		[Out, SizeDef(nameof(pcbDecoded), SizingMethod.Query)] IntPtr pbDecoded, ref uint pcbDecoded, out SafePCCERT_CONTEXT ppSignerCert);

	/// <summary>
	/// The <c>CryptVerifyMessageSignatureWithKey</c> function verifies a signed message's signature by using specified public key information.
	/// </summary>
	/// <param name="pVerifyPara">A pointer to a CRYPT_KEY_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
	/// <param name="pPublicKeyInfo">
	/// A pointer to a CERT_PUBLIC_KEY_INFO structure that contains the public key that is used to verify the signed message. If
	/// <c>NULL</c>, the signature is not verified.
	/// </param>
	/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
	/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
	/// <param name="pbDecoded">
	/// <para>A pointer to a buffer to receive the decoded message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
	/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecoded">
	/// <para>
	/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
	/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero.</para>
	/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
	/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pVerifyPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a signed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_SIGNER</term>
	/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_SIGNATURE</term>
	/// <term>The message's signature was not verified.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignaturewithkey BOOL
	// CryptVerifyMessageSignatureWithKey( PCRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, PCERT_PUBLIC_KEY_INFO pPublicKeyInfo, const BYTE
	// *pbSignedBlob, DWORD cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "6fe0f9ee-1838-4eb7-8254-05b878eb8f56")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyMessageSignatureWithKey(in CRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, in CERT_PUBLIC_KEY_INFO pPublicKeyInfo,
		[In, SizeDef(nameof(cbSignedBlob))] IntPtr pbSignedBlob, uint cbSignedBlob,
		[Out, SizeDef(nameof(pcbDecoded), SizingMethod.Query)] IntPtr pbDecoded, ref uint pcbDecoded);

	/// <summary>
	/// The <c>CryptVerifyMessageSignatureWithKey</c> function verifies a signed message's signature by using specified public key information.
	/// </summary>
	/// <param name="pVerifyPara">A pointer to a CRYPT_KEY_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
	/// <param name="pPublicKeyInfo">
	/// A pointer to a CERT_PUBLIC_KEY_INFO structure that contains the public key that is used to verify the signed message. If
	/// <c>NULL</c>, the signature is not verified.
	/// </param>
	/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
	/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
	/// <param name="pbDecoded">
	/// <para>A pointer to a buffer to receive the decoded message.</para>
	/// <para>
	/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
	/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
	/// </para>
	/// </param>
	/// <param name="pcbDecoded">
	/// <para>
	/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
	/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
	/// </para>
	/// <para>
	/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
	/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
	/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
	/// updated to reflect the actual size of the data copied to the buffer.
	/// </para>
	/// </param>
	/// <returns>
	/// <para>If the function succeeds, the function returns nonzero.</para>
	/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
	/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
	/// <list type="table">
	/// <listheader>
	/// <term>Return code</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <term>ERROR_MORE_DATA</term>
	/// <term>
	/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
	/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
	/// </term>
	/// </item>
	/// <item>
	/// <term>E_INVALIDARG</term>
	/// <term>
	/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
	/// Invalid cbSize in *pVerifyPara.
	/// </term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
	/// <term>Not a signed cryptographic message.</term>
	/// </item>
	/// <item>
	/// <term>CRYPT_E_NO_SIGNER</term>
	/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_ALGID</term>
	/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
	/// </item>
	/// <item>
	/// <term>NTE_BAD_SIGNATURE</term>
	/// <term>The message's signature was not verified.</term>
	/// </item>
	/// </list>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignaturewithkey BOOL
	// CryptVerifyMessageSignatureWithKey( PCRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, PCERT_PUBLIC_KEY_INFO pPublicKeyInfo, const BYTE
	// *pbSignedBlob, DWORD cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded );
	[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
	[PInvokeData("wincrypt.h", MSDNShortId = "6fe0f9ee-1838-4eb7-8254-05b878eb8f56")]
	[return: MarshalAs(UnmanagedType.Bool)]
	public static extern bool CryptVerifyMessageSignatureWithKey(in CRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, [In, Optional] IntPtr pPublicKeyInfo,
		[In, SizeDef(nameof(cbSignedBlob))] IntPtr pbSignedBlob, uint cbSignedBlob,
		[Out, SizeDef(nameof(pcbDecoded), SizingMethod.Query)] IntPtr pbDecoded, ref uint pcbDecoded);

	/// <summary>
	/// The <c>CMSG_CMS_SIGNER_INFO</c> structure contains the content of the defined SignerInfo in signed or signed and enveloped
	/// messages. In decoding a received message, CryptMsgGetParam is called for each signer to get a <c>CMSG_CMS_SIGNER_INFO</c> structure.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_cms_signer_info typedef struct _CMSG_CMS_SIGNER_INFO
	// { DWORD dwVersion; CERT_ID SignerId; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; CRYPT_ALGORITHM_IDENTIFIER
	// HashEncryptionAlgorithm; CRYPT_DATA_BLOB EncryptedHash; CRYPT_ATTRIBUTES AuthAttrs; CRYPT_ATTRIBUTES UnauthAttrs; }
	// CMSG_CMS_SIGNER_INFO, *PCMSG_CMS_SIGNER_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "177323ef-4e26-4681-a474-1a99fb6900af")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CMS_SIGNER_INFO
	{
		/// <summary>The version of this structure.</summary>
		public uint dwVersion;

		/// <summary>A CERT_ID structure that identifies the signer's certificate.</summary>
		public CERT_ID SignerId;

		/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm used in generating the hash of a message.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm used to encrypt the hash.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

		/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted hash of the message, the signature.</summary>
		public CRYPTOAPI_BLOB EncryptedHash;

		/// <summary>A CRYPT_ATTRIBUTES structure that contains authenticated attributes of the signer.</summary>
		public CRYPT_ATTRIBUTES AuthAttrs;

		/// <summary>A CRYPT_ATTRIBUTES structure that contains unauthenticated attributes of the signer.</summary>
		public CRYPT_ATTRIBUTES UnauthAttrs;
	}

	/// <summary>
	/// The <c>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA</c> structure is used to add an unauthenticated attribute to a signer of a signed
	/// message. This structure is passed to CryptMsgControl if the dwCtrlType parameter is set to <c>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR</c>.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_add_signer_unauth_attr_para typedef struct
	// _CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA { DWORD cbSize; DWORD dwSignerIndex; CRYPT_DATA_BLOB blob; }
	// CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA, *PCMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "5e347a50-942e-4278-a9ae-ad4c30c55c6b")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// Index of the signer in the <c>rgSigners</c> array of pointers of CMSG_SIGNER_ENCODE_INFO structures in a signed message's
		/// CMSG_SIGNED_ENCODE_INFO structure. The unauthenticated attribute is to be added to this signer's information.
		/// </summary>
		public uint dwSignerIndex;

		/// <summary/>
		public CRYPTOAPI_BLOB blob;
	}

	/// <summary>
	/// <para>
	/// The <c>CMSG_CTRL_DECRYPT_PARA</c> structure contains information used to decrypt an enveloped message for a key transport
	/// recipient. This structure is passed to CryptMsgControl if the dwCtrlType parameter is CMSG_CTRL_DECRYPT.
	/// </para>
	/// <para>
	/// For information about how CryptoAPI supports Secure/Multipurpose Internet Mail Extensions (S/MIME) email interoperability, see
	/// the Remarks section of CryptMsgOpenToEncode.
	/// </para>
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_decrypt_para typedef struct
	// _CMSG_CTRL_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME; DWORD
	// dwKeySpec; DWORD dwRecipientIndex; } CMSG_CTRL_DECRYPT_PARA, *PCMSG_CTRL_DECRYPT_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "eb9b1daa-b04f-419a-88e3-7c772f9e62eb")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_DECRYPT_PARA
	{
		/// <summary>The size, in bytes, of this structure.</summary>
		public uint cbSize;

		/// <summary>
		/// Cryptographic service provider (CSP) handle. The CNG function NCryptIsKeyHandle is called to determine the union choice.
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// A handle to the CNG Cryptographic service provider (CSP). The CNG function, NCryptIsKeyHandle, is called to determine
		/// the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function, NCryptTranslateHandle,
		/// will be called to convert the CryptoAPI hCryptProv choice where necessary. We recommend that applications pass, to the
		/// hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
		/// </summary>
		public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey { readonly get => (NCrypt.NCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary>
		/// <para>The private key to be used. This member is not used when the hNCryptKey member is used.</para>
		/// <para>The following <c>dwKeySpec</c> values are defined for the default provider.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>AT_KEYEXCHANGE</term>
		/// <term>Keys used to encrypt and decrypt session keys.</term>
		/// </item>
		/// <item>
		/// <term>AT_SIGNATURE</term>
		/// <term>Keys used to create and verify digital signatures.</term>
		/// </item>
		/// </list>
		/// <para>If <c>dwKeySpec</c> is zero, the default AT_KEYEXCHANGE is used.</para>
		/// </summary>
		public CertKeySpec dwKeySpec;

		/// <summary>Index of the recipient in the message associated with the <c>hCryptProv</c> private key.</summary>
		public uint dwRecipientIndex;
	}

	/// <summary>
	/// The <c>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA</c> structure is used to delete an unauthenticated attribute of a signer of a
	/// signed message. This structure is passed to CryptMsgControl if the dwCrlType parameter is <c>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR</c>.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_del_signer_unauth_attr_para typedef struct
	// _CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA { DWORD cbSize; DWORD dwSignerIndex; DWORD dwUnauthAttrIndex; }
	// CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA, *PCMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "729fbbe0-40c6-41e7-851f-6f93f47e8f4d")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// Index of the signer in the <c>rgSigners</c> array of pointers to CMSG_SIGNER_ENCODE_INFO structures in a signed message's
		/// CMSG_SIGNED_ENCODE_INFO structure. The unauthenticated attribute for this signer is deleted.
		/// </summary>
		public uint dwSignerIndex;

		/// <summary>
		/// Index of the element in the <c>rgUnauthAttr</c> array of the CMSG_SIGNER_ENCODE_INFO structure holding the unauthenticated
		/// attribute to be removed.
		/// </summary>
		public uint dwUnauthAttrIndex;
	}

	/// <summary>The <c>CMSG_CTRL_KEY_AGREE_DECRYPT_PARA</c> structure contains information about a key agreement recipient.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_key_agree_decrypt_para typedef struct
	// _CMSG_CTRL_KEY_AGREE_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME;
	// DWORD dwKeySpec; PCMSG_KEY_AGREE_RECIPIENT_INFO pKeyAgree; DWORD dwRecipientIndex; DWORD dwRecipientEncryptedKeyIndex;
	// CRYPT_BIT_BLOB OriginatorPublicKey; } CMSG_CTRL_KEY_AGREE_DECRYPT_PARA, *PCMSG_CTRL_KEY_AGREE_DECRYPT_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "14e58281-4315-4ece-8ea8-92765cffd212")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_KEY_AGREE_DECRYPT_PARA
	{
		/// <summary>The size, in bytes, of this data structure.</summary>
		public uint cbSize;

		/// <summary>
			/// A handle to the cryptographic service provider (CSP) used to do the recipient key encryption and export. If <c>NULL</c>,
			/// the provider specified in CMSG_ENVELOPED_ENCODE_INFO is used. The CNG function NCryptIsKeyHandle is called to determine
			/// the union choice.
			/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// A handle to the CNG CSP used to do the recipient key encryption and export. The CNG function NCryptIsKeyHandle is called
		/// to determine the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function
		/// NCryptTranslateHandle will be called to convert the CryptoAPI CSP hCryptProv choice where necessary. We recommend that
		/// applications pass, to the hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
		/// </summary>
		public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey { readonly get => (NCrypt.NCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary>
		/// Specifies the encrypted key. The encrypted key is the result of encrypting the content-encryption key. This member is not
		/// used when the hNCryptKey member is used.
		/// </summary>
		public CertKeySpec dwKeySpec;

		/// <summary>A pointer to a CMSG_KEY_AGREE_RECIPIENT_INFO structure.</summary>
		public StructPointer<CMSG_KEY_AGREE_RECIPIENT_INFO> pKeyAgree;

		/// <summary>Indicates a specific recipient in an array of recipients.</summary>
		public uint dwRecipientIndex;

		/// <summary>Indicates a specific encrypted key in an array of encrypted keys.</summary>
		public uint dwRecipientEncryptedKeyIndex;

		/// <summary>A CRYPT_BIT_BLOB structure that contains the sender's public key information.</summary>
		public CRYPTOAPI_BLOB OriginatorPublicKey;
	}

	/// <summary>The <c>CMSG_CTRL_KEY_TRANS_DECRYPT_PARA</c> structure contains information about a key transport message recipient.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_key_trans_decrypt_para typedef struct
	// _CMSG_CTRL_KEY_TRANS_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME;
	// DWORD dwKeySpec; PCMSG_KEY_TRANS_RECIPIENT_INFO pKeyTrans; DWORD dwRecipientIndex; } CMSG_CTRL_KEY_TRANS_DECRYPT_PARA, *PCMSG_CTRL_KEY_TRANS_DECRYPT_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "5f3387c9-4ff0-42a0-8fc7-67d3bb8b6bef")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_KEY_TRANS_DECRYPT_PARA
	{
		/// <summary>The size, in bytes, of this data structure.</summary>
		public uint cbSize;

		/// <summary>
		/// A handle to the cryptographic service provider (CSP) used to do the recipient key encryption and export. If <c>NULL</c>,
		/// the provider specified in CMSG_ENVELOPED_ENCODE_INFO is used. The CNG function NCryptIsKeyHandle is called to determine
		/// the union choice.
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// A handle to the CNG CSP used to do the recipient key encryption and export. The CNG function NCryptIsKeyHandle is called
		/// to determine the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function
		/// NCryptTranslateHandle will be called to convert the CryptoAPI CSP hCryptProv choice where necessary. We recommend that
		/// applications pass, to the hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
		/// </summary>
		public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey { readonly get => (NCrypt.NCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary>
		/// Specifies the encrypted key. The encrypted key is the result of encrypting the content-encryption key for a specific
		/// recipient by using that recipient's public key. This member is not used when the hNCryptKey member is used.
		/// </summary>
		public uint dwKeySpec;

		/// <summary>A pointer to a CMSG_KEY_TRANS_RECIPIENT_INFO structure.</summary>
		public StructPointer<CMSG_KEY_TRANS_RECIPIENT_INFO> pKeyTrans;

		/// <summary>Indicates a specific recipient in any array of recipients.</summary>
		public uint dwRecipientIndex;
	}

	/// <summary>The <c>CMSG_CTRL_MAIL_LIST_DECRYPT_PARA</c> structure contains information on a mail list message recipient.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_mail_list_decrypt_para typedef struct
	// _CMSG_CTRL_MAIL_LIST_DECRYPT_PARA { DWORD cbSize; HCRYPTPROV hCryptProv; PCMSG_MAIL_LIST_RECIPIENT_INFO pMailList; DWORD
	// dwRecipientIndex; DWORD dwKeyChoice; union { HCRYPTKEY hKeyEncryptionKey; void *pvKeyEncryptionKey; } DUMMYUNIONNAME; }
	// CMSG_CTRL_MAIL_LIST_DECRYPT_PARA, *PCMSG_CTRL_MAIL_LIST_DECRYPT_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "30735e01-db6b-40fc-b4c8-cdc24e73defa")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_MAIL_LIST_DECRYPT_PARA
	{
		/// <summary>The size, in bytes, of this data structure.</summary>
		public uint cbSize;

		/// <summary>
		/// The provider used to do the recipient key encryption and export. If <c>hCryptProv</c> is <c>NULL</c>, the provider specified
		/// in CMSG_ENVELOPED_ENCODE_INFO is used.
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>A pointer to a CMSG_MAIL_LIST_RECIPIENT_INFO structure.</summary>
		public StructPointer<CMSG_MAIL_LIST_RECIPIENT_INFO> pMailList;

		/// <summary>Indicates a specific recipient in any array of recipients.</summary>
		public uint dwRecipientIndex;

		/// <summary>
		/// Indicates the member of the following union that will be used. Currently only CMSG_MAIL_LIST_HANDLE_KEY_CHOICE is defined.
		/// </summary>
		public uint dwKeyChoice;

		/// <summary>Handle of the key encryption key. Used with <c>dwKeyChoice</c> set to CMSG_MAIL_LIST_HANDLE_KEY_CHOICE.</summary>
		public HCRYPTKEY hKeyEncryptionKey;
	}

	/// <summary>
	/// The <c>CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA</c> structure contains information used to verify a message signature. It contains the
	/// signer index and signer public key. The signer public key can be the signer's CERT_PUBLIC_KEY_INFO structure, certificate
	/// context, or chain context.
	/// </summary>
	/// <remarks>
	/// If <c>dwSignerType</c> is CMSG_VERIFY_SIGNER_NULL, the signature is expected to contain only the unencrypted hash octets.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_verify_signature_ex_para typedef struct
	// _CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA { DWORD cbSize; HCRYPTPROV_LEGACY hCryptProv; DWORD dwSignerIndex; DWORD dwSignerType; void
	// *pvSigner; } CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA, *PCMSG_CTRL_VERIFY_SIGNATURE_EX_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "56b73de8-c170-46f6-b488-096475b59c15")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA
	{
		/// <summary>The size, in bytes, of this structure.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>This member is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic provider used to verify the signature. If
		/// <c>NULL</c>, the cryptographic provider specified in CryptMsgOpenToDecode is used. If the hCryptProv in
		/// <c>CryptMsgOpenToDecode</c> is also <c>NULL</c>, the default provider according to the signer's public key object identifier
		/// (OID) is used.This member's data type is <c>HCRYPTPROV</c>.
		/// </para>
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>The index of the signer in the message.</summary>
		public uint dwSignerIndex;

		/// <summary>
		/// <para>
		/// The structure that contains the signer information. The following table shows the predefined values and the structures indicated.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_PUBKEY</term>
		/// <term>CERT_PUBLIC_KEY_INFO</term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_CERT</term>
		/// <term>CERT_CONTEXT</term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_CHAIN</term>
		/// <term>CERT_CHAIN_CONTEXT</term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_NULL</term>
		/// <term>NULL</term>
		/// </item>
		/// </list>
		/// </summary>
		public CryptMsgSignerType dwSignerType;

		/// <summary>
		/// A pointer to a CERT_PUBLIC_KEY_INFO structure, a certificate context, a chain context, or <c>NULL</c> depending on the value
		/// of <c>dwSignerType</c>.
		/// </summary>
		public IntPtr pvSigner;
	}

	/// <summary>The <c>CMSG_KEY_AGREE_RECIPIENT_INFO</c> structure contains information used for key agreement algorithms.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_key_agree_recipient_info typedef struct
	// _CMSG_KEY_AGREE_RECIPIENT_INFO { DWORD dwVersion; DWORD dwOriginatorChoice; union { CERT_ID OriginatorCertId;
	// CERT_PUBLIC_KEY_INFO OriginatorPublicKeyInfo; } DUMMYUNIONNAME; CRYPT_DATA_BLOB UserKeyingMaterial; CRYPT_ALGORITHM_IDENTIFIER
	// KeyEncryptionAlgorithm; DWORD cRecipientEncryptedKeys; PCMSG_RECIPIENT_ENCRYPTED_KEY_INFO *rgpRecipientEncryptedKeys; }
	// CMSG_KEY_AGREE_RECIPIENT_INFO, *PCMSG_KEY_AGREE_RECIPIENT_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "d29d04d6-065e-4bb7-843b-f563643eeb4c")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_KEY_AGREE_RECIPIENT_INFO
	{
		/// <summary>A <c>DWORD</c> that indicates the version of the structure. Always set to three.</summary>
		public uint dwVersion;

		/// <summary>A <c>DWORD</c> that indicates the key identifier to use.</summary>
		public CryptMsgKeyOriginator dwOriginatorChoice;

		/// <summary/>
		public CMSG_KEY_AGREE_RECIPIENT_INFO_UNION Originator;

		/// <summary/>
		[StructLayout(LayoutKind.Explicit)]
		public struct CMSG_KEY_AGREE_RECIPIENT_INFO_UNION
		{
			/// <summary>A CERT_ID that identifies the public key of the message originator.</summary>
			[FieldOffset(0)]
			public CERT_ID OriginatorCertId;

			/// <summary>A CERT_PUBLIC_KEY_INFO structure that contains the public key of the message originator.</summary>
			[FieldOffset(0)]
			public CERT_PUBLIC_KEY_INFO OriginatorPublicKeyInfo;
		}

		/// <summary>
		/// A CRYPT_DATA_BLOB that indicates that a different key is generated each time the same two parties generate a pair of keys.
		/// The sender provides the bits of this BLOB with some key agreement algorithms. This member can be <c>NULL</c>.
		/// </summary>
		public CRYPTOAPI_BLOB UserKeyingMaterial;

		/// <summary>
		/// A CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
		/// content encryption key.
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

		/// <summary>The number of elements in the <c>rgpRecipientEncryptedKeys</c> array.</summary>
		public uint cRecipientEncryptedKeys;

		/// <summary>
		/// The address of an array of CMSG_RECIPIENT_ENCRYPTED_KEY_INFO structures that contains information about the key recipients.
		/// The <c>cRecipientEncryptedKeys</c> member contains the number of elements in this array.
		/// </summary>
		public ArrayPointer<CMSG_RECIPIENT_ENCRYPTED_KEY_INFO> rgpRecipientEncryptedKeys;
	}

	/// <summary>The <c>CMSG_KEY_TRANS_RECIPIENT_INFO</c> structure contains information used in key transport algorithms.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_key_trans_recipient_info typedef struct
	// _CMSG_KEY_TRANS_RECIPIENT_INFO { DWORD dwVersion; CERT_ID RecipientId; CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;
	// CRYPT_DATA_BLOB EncryptedKey; } CMSG_KEY_TRANS_RECIPIENT_INFO, *PCMSG_KEY_TRANS_RECIPIENT_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "956b0646-50a5-46d1-aa9a-91194c35d2b2")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_KEY_TRANS_RECIPIENT_INFO
	{
		/// <summary>
		/// Indicates the version of the structure. If <c>RecipientId</c> uses the ISSUER_SERIAL_NUMBER to identify the recipient,
		/// <c>dwVersion</c> is set to zero. If <c>RecipientId</c> uses KEYID, <c>dwVersion</c> is set to two.
		/// </summary>
		public uint dwVersion;

		/// <summary>
		/// A CERT_ID that identifies the recipient. Currently, only ISSUER_SERIAL_NUMBER or KEYID choices in the <c>CERT_ID</c> are valid.
		/// </summary>
		public CERT_ID RecipientId;

		/// <summary>
		/// A CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
		/// content encryption key.
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

		/// <summary>A CRYPT_DATA_BLOB that contains the bytes of the encrypted session key.</summary>
		public CRYPTOAPI_BLOB EncryptedKey;
	}

	/// <summary>
	/// The <c>CMSG_MAIL_LIST_RECIPIENT_INFO</c> structure contains information used for previously distributed symmetric key-encryption
	/// keys (KEK).
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_mail_list_recipient_info typedef struct
	// _CMSG_MAIL_LIST_RECIPIENT_INFO { DWORD dwVersion; CRYPT_DATA_BLOB KeyId; CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;
	// CRYPT_DATA_BLOB EncryptedKey; FILETIME Date; PCRYPT_ATTRIBUTE_TYPE_VALUE pOtherAttr; } CMSG_MAIL_LIST_RECIPIENT_INFO, *PCMSG_MAIL_LIST_RECIPIENT_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "e0946278-75e9-4990-af81-d9e61da9724b")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_MAIL_LIST_RECIPIENT_INFO
	{
		/// <summary>Indicates the version of the structure. This member is always four.</summary>
		public uint dwVersion;

		/// <summary>
		/// A CRYPT_DATA_BLOB structure that identifies a symmetric key-encryption key previously distributed to the sender and one or
		/// more recipients.
		/// </summary>
		public CRYPTOAPI_BLOB KeyId;

		/// <summary>
		/// CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
		/// content encryption key.
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

		/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted content encryption key.</summary>
		public CRYPTOAPI_BLOB EncryptedKey;

		/// <summary>Optional. When present, this member specifies a single key-encryption key from a previously distributed set.</summary>
		public FILETIME Date;

		/// <summary>Optional pointer to a CRYPT_ATTRIBUTE_TYPE_VALUE structure containing additional information.</summary>
		public StructPointer<CRYPT_ATTRIBUTE_TYPE_VALUE> pOtherAttr;
	}

	/// <summary>
	/// The <c>CMSG_RECIPIENT_ENCRYPTED_KEY_INFO</c> structure contains information used for an individual key agreement recipient.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_recipient_encrypted_key_info typedef struct
	// _CMSG_RECIPIENT_ENCRYPTED_KEY_INFO { CERT_ID RecipientId; CRYPT_DATA_BLOB EncryptedKey; FILETIME Date;
	// PCRYPT_ATTRIBUTE_TYPE_VALUE pOtherAttr; } CMSG_RECIPIENT_ENCRYPTED_KEY_INFO, *PCMSG_RECIPIENT_ENCRYPTED_KEY_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "1921f9b6-86d9-47a0-a36e-e20d481382a3")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_RECIPIENT_ENCRYPTED_KEY_INFO
	{
		/// <summary>
		/// CERT_ID structure identifying the recipient. Currently, only the ISSUER_SERIAL_NUMBER or KEYID choices in the <c>CERT_ID</c>
		/// structure are valid.
		/// </summary>
		public CERT_ID RecipientId;

		/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted content encryption key.</summary>
		public CRYPTOAPI_BLOB EncryptedKey;

		/// <summary>
		/// Optional. When present, this member specifies which of the recipient's previously distributed UKMs was used by the sender.
		/// Only applicable to KEYID choice in the <c>RecipientId</c> CERT_ID structure.
		/// </summary>
		public FILETIME Date;

		/// <summary>
		/// Optional pointer to a CRYPT_ATTRIBUTE_TYPE_VALUE structure containing additional information. Only applicable to KEYID
		/// choice in the <c>RecipientId</c> CERT_ID structure.
		/// </summary>
		public StructPointer<CRYPT_ATTRIBUTE_TYPE_VALUE> pOtherAttr;
	}

	/// <summary>
	/// The <c>CMSG_SIGNER_ENCODE_INFO</c> structure contains signer information. It is passed to CryptMsgCountersign,
	/// CryptMsgCountersignEncoded, and optionally to CryptMsgOpenToEncode as a member of the CMSG_SIGNED_ENCODE_INFO structure, if the
	/// dwMsgType parameter is CMSG_SIGNED.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_signer_encode_info typedef struct
	// _CMSG_SIGNER_ENCODE_INFO { DWORD cbSize; PCERT_INFO pCertInfo; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey;
	// BCRYPT_KEY_HANDLE hBCryptKey; } DUMMYUNIONNAME; DWORD dwKeySpec; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; void *pvHashAuxInfo;
	// DWORD cAuthAttr; PCRYPT_ATTRIBUTE rgAuthAttr; DWORD cUnauthAttr; PCRYPT_ATTRIBUTE rgUnauthAttr; CERT_ID SignerId;
	// CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; void *pvHashEncryptionAuxInfo; } CMSG_SIGNER_ENCODE_INFO, *PCMSG_SIGNER_ENCODE_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "f599226d-ddd7-455f-b650-74b91674d8f9")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_SIGNER_ENCODE_INFO
	{
		/// <summary>The size, in bytes, of this structure.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>A pointer to a CERT_INFO structure that contains the</para>
		/// <para><c>Issuer</c>, <c>SerialNumber</c>, and <c>SubjectPublicKeyInfo</c> members.</para>
		/// <para>
		/// The <c>pbData</c> members of the <c>Issuer</c> and <c>SerialNumber</c> structures combined uniquely identify a certificate.
		/// The <c>Algorithm</c> member of the <c>SubjectPublicKeyInfo</c> structure specifies the hash encryption algorithm used.
		/// </para>
		/// </summary>
		public StructPointer<CERT_INFO> pCertInfo;

		/// <summary>
		/// A handle to the cryptographic service provider (CSP). If <c>HashEncryptionAlgorithm</c> is set to
		/// szOID_PKIX_NO_SIGNATURE, this handle can be the handle of a CSP acquired by using the dwFlags parameter set to
		/// <c>CRYPT_VERIFYCONTEXT</c>. The CNG function NCryptIsKeyHandle is called to determine the union choice.
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// A handle to the CNG CSP. The CNG function NCryptIsKeyHandle is called to determine the union choice. New encrypt
		/// algorithms are only supported in CNG functions. The CNG function NCryptTranslateHandle will be called to convert the
		/// CryptoAPI hCryptProv choice where necessary. We recommend that applications pass, to the hNCryptKey member, the CNG CSP
		/// handle that is returned from the NCryptOpenKey function.
		/// </summary>
		public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey { readonly get => (NCrypt.NCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary/>
		public BCrypt.BCRYPT_KEY_HANDLE hBCryptKey { readonly get => (BCrypt.BCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary>
		/// <para>Specifies the private key to be used. This member is not used when the hNCryptKey member is used.</para>
		/// <para>If <c>dwKeySpec</c> is zero, then the default AT_KEYEXCHANGE value is used.</para>
		/// <para>The following <c>dwKeySpec</c> values are defined for the default provider.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>AT_KEYEXCHANGE</term>
		/// <term>Keys used to encrypt/decrypt session keys.</term>
		/// </item>
		/// <item>
		/// <term>AT_SIGNATURE</term>
		/// <term>Keys used to create and verify digital signatures.</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertKeySpec dwKeySpec;

		/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the hash algorithm.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>Not used. This member must be set to <c>NULL</c>.</summary>
		public IntPtr pvHashAuxInfo;

		/// <summary>
		/// The number of elements in the <c>rgAuthAttr</c> array. If no authenticated attributes are present in <c>rgAuthAttr</c>, then
		/// <c>cAuthAttr</c> is zero.
		/// </summary>
		public uint cAuthAttr;

		/// <summary>
		/// <para>An array of pointers to CRYPT_ATTRIBUTE structures, each of which contains authenticated attribute information.</para>
		/// <para>
		/// The PKCS #9 standard dictates that if there are any attributes, there must be at least two: the content type object
		/// identifier (OID) and the hash of the message. These attributes are automatically added by the system.
		/// </para>
		/// </summary>
		public ArrayPointer<CRYPT_ATTRIBUTE> rgAuthAttr;

		/// <summary>
		/// The number of elements in the <c>rgUnauthAttr</c> array. If there are no unauthenticated attributes, <c>cUnauthAttr</c> is zero.
		/// </summary>
		public uint cUnauthAttr;

		/// <summary>
		/// An array of pointers to CRYPT_ATTRIBUTE structures, each of which contains unauthenticated attribute information.
		/// Unauthenticated attributes can contain countersignatures, among other uses.
		/// </summary>
		public ArrayPointer<CRYPT_ATTRIBUTE> rgUnauthAttr;

		/// <summary>
		/// A CERT_ID structure that contains a unique identifier of the signer's certificate. This member can optionally be used with
		/// PKCS #7 with Cryptographic Message Syntax (CMS). If this member is not <c>NULL</c> and its <c>dwIdChoice</c> member is not
		/// zero, it is used to identify the certificate instead of the <c>Issuer</c> and <c>SerialNumber</c> members of the CERT_INFO
		/// structure pointed to by <c>pCertInfo</c>. CMS supports the KEY_IDENTIFIER and ISSUER_SERIAL_NUMBER CERT_ID structures. PKCS
		/// version 1.5 supports only the ISSUER_SERIAL_NUMBER CERT_ID choice. This member is used with CMS for PKCS #7 processing and
		/// can be used only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
		/// </summary>
		public CERT_ID SignerId;

		/// <summary>
		/// <para>
		/// A CRYPT_ALGORITHM_IDENTIFIER structure optionally used with PKCS #7 with CMS. If this member is not <c>NULL</c>, the
		/// algorithm identified is used instead of the SubjectPublicKeyInfo.Algorithm algorithm. If this member is set to
		/// szOID_PKIX_NO_SIGNATURE, the signature value contains only the hash octets.
		/// </para>
		/// <para>
		/// For RSA, the hash encryption algorithm is normally the same as the public key algorithm. For DSA, the hash encryption
		/// algorithm is normally a DSS signature algorithm.
		/// </para>
		/// <para>
		/// This member is used with CMS for PKCS #7 processing and can be used only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
		/// </para>
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

		/// <summary>
		/// This member is not used. This member must be set to <c>NULL</c> if it is present in the data structure. This member is
		/// present only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
		/// </summary>
		public IntPtr pvHashEncryptionAuxInfo;
	}

	/// <summary>
	/// The <c>CMSG_SIGNER_INFO</c> structure contains the content of the PKCS #7 defined SignerInfo in signed messages. In decoding a
	/// received message, CryptMsgGetParam is called for each signer to get a <c>CMSG_SIGNER_INFO</c> structure.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_signer_info typedef struct _CMSG_SIGNER_INFO { DWORD
	// dwVersion; CERT_NAME_BLOB Issuer; CRYPT_INTEGER_BLOB SerialNumber; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
	// CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; CRYPT_DATA_BLOB EncryptedHash; CRYPT_ATTRIBUTES AuthAttrs; CRYPT_ATTRIBUTES
	// UnauthAttrs; } CMSG_SIGNER_INFO, *PCMSG_SIGNER_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "NS:wincrypt._CMSG_SIGNER_INFO")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_SIGNER_INFO
	{
		/// <summary>The version of this structure.</summary>
		public uint dwVersion;

		/// <summary>A CERT_NAME_BLOB structure that contains the issuer of a certificate with the public key needed to verify a signature.</summary>
		public CRYPTOAPI_BLOB Issuer;

		/// <summary>
		/// A CRYPT_INTEGER_BLOB structure that contains the serial number of the certificate that contains the public key needed to
		/// verify a signature. For more information, see CERT_INFO.
		/// </summary>
		public CRYPTOAPI_BLOB SerialNumber;

		/// <summary>CRYPT_ALGORITHM_IDENTIFIER structure specifying the algorithm used in generating the hash of a message.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>CRYPT_ALGORITHM_IDENTIFIER structure specifying the algorithm used to encrypt the hash.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

		/// <summary>A CRYPT_DATA_BLOB that contains the encrypted hash of the message, the signature.</summary>
		public CRYPTOAPI_BLOB EncryptedHash;

		/// <summary>CRYPT_ATTRIBUTES structure containing authenticated attributes of the signer.</summary>
		public CRYPT_ATTRIBUTES AuthAttrs;

		/// <summary>CRYPT_ATTRIBUTES structure containing unauthenticated attributes of the signer.</summary>
		public CRYPT_ATTRIBUTES UnauthAttrs;
	}

	/// <summary>
	/// <para>
	/// The <c>CMSG_STREAM_INFO</c> structure is used to enable stream processing of data rather than single block processing. Stream
	/// processing is most often used when processing large messages. Stream-processed messages can originate from any serialized source
	/// such as a file on a hard disk, a server, or a CD ROM.
	/// </para>
	/// <para>This structure is passed to the CryptMsgOpenToEncode and CryptMsgOpenToDecode functions.</para>
	/// </summary>
	/// <remarks>
	/// <para>
	/// Messages can be so large that processing them all at once by storing the whole message in memory can be difficult, if not
	/// impossible. It is possible to process large messages without encountering memory limitations by streaming the data that is to be
	/// processed into manageable sized blocks. The low-level message functions can be used with streaming to encode or decode a
	/// message. Any level of nesting of messages is supported when streaming to encode and streaming to decode.
	/// </para>
	/// <para>
	/// The input message to be processed as a stream feeds into CryptMsgUpdate one block at a time, with the application determining
	/// the size of the block. As the streamed message is processed for encoding or decoding, the resulting output data is passed back
	/// to the application through an application-specified callback function that is specified by the <c>pfnStreamOutput</c> member.
	/// </para>
	/// <para>
	/// No assumptions can be made about the block size of the output data because the size can vary for several reasons, such as the
	/// jitter in output block size caused by the block size for the encryption algorithm when processing an enveloped message, or when
	/// blocks that contain the message header and the SignerInfo as defined by PKCS # 7 are processed.
	/// </para>
	/// <para>
	/// The size of the output block is passed to the callback function in its cbData parameter. The use of output data is determined in
	/// the calling application. Typically, output from stream processing will not be persisted in memory as a whole due to memory
	/// limitations; rather, it will be serialized to a disk or server file.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_stream_info typedef struct _CMSG_STREAM_INFO { DWORD
	// cbContent; PFN_CMSG_STREAM_OUTPUT pfnStreamOutput; void *pvArg; } CMSG_STREAM_INFO, *PCMSG_STREAM_INFO;
	[PInvokeData("wincrypt.h", MSDNShortId = "a4e7f6e8-351f-4981-b223-50b65f503394")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CMSG_STREAM_INFO
	{
		/// <summary>
		/// Specifies the size, in bytes, of the content. Normal Distinguished Encoding Rules (DER) encoding is used unless
		/// <c>CMSG_INDEFINITE_LENGTH</c>(0xFFFFFFFF) is passed, indicating that the application is not specifying the content length.
		/// This forces the use of indefinite-length Basic Encoding Rules (BER) encoding.
		/// </summary>
		public uint cbContent;

		/// <summary>
		/// <para>The address of a callback function used to read from and write data to a disk when processing large messages.</para>
		/// <para>The callback function must have the following signature and parameters:</para>
		/// </summary>
		public PFN_CMSG_STREAM_OUTPUT pfnStreamOutput;

		/// <summary>
		/// A pointer to the argument to pass to the callback function. Typically, this is used for state data that includes the handle
		/// to a more deeply nested message (when decoding) or a less deeply nested message (when encoding).
		/// </summary>
		public IntPtr pvArg;
	}

	/// <summary>The <c>CRYPT_DECRYPT_MESSAGE_PARA</c> structure contains information for decrypting messages.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_decrypt_message_para typedef struct
	// _CRYPT_DECRYPT_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; DWORD cCertStore; HCERTSTORE *rghCertStore; DWORD
	// dwFlags; } CRYPT_DECRYPT_MESSAGE_PARA, *PCRYPT_DECRYPT_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "67e136cd-12e3-4a31-9d8b-b53e1129e940")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_DECRYPT_MESSAGE_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
		/// with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgAndCertEncodingType;

		/// <summary>Number of elements in the <c>rghCertStore</c> array.</summary>
		public uint cCertStore;

		/// <summary>
		/// <para>Array of certificate store handles.</para>
		/// <para>
		/// These certificate store handles are used to obtain the certificate context to use for decrypting a message. For more
		/// information, see the decryption functions CryptDecryptMessage, and CryptDecryptAndVerifyMessageSignature. An encrypted
		/// message can have one or more recipients. The recipients are identified by a unique certificate identifier, often the hash of
		/// the certificate issuer and serial number. The certificate stores are searched to find a certificate context corresponding to
		/// the unique identifier.
		/// </para>
		/// <para>
		/// Recipients can also be identified by their KeyId. Both Key Agreement (Diffie-Hellman) and Key Transport (RSA) recipients are supported.
		/// </para>
		/// <para>
		/// Only certificate contexts in the store with one of the following properties, CERT_KEY_PROV_INFO_PROP_ID, or
		/// CERT_KEY_CONTEXT_PROP_ID can be used. These properties specify the location of a needed private exchange key.
		/// </para>
		/// </summary>
		public ArrayPointer<HCERTSTORE> rghCertStore;

		/// <summary>
		/// The CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT
		/// flag, see CryptAcquireContext.
		/// </summary>
		public CryptMsgActionFlags dwFlags;
	}

	/// <summary>The <c>CRYPT_ENCRYPT_MESSAGE_PARA</c> structure contains information used to encrypt messages.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_encrypt_message_para typedef struct
	// _CRYPT_ENCRYPT_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; CRYPT_ALGORITHM_IDENTIFIER
	// ContentEncryptionAlgorithm; void *pvEncryptionAuxInfo; DWORD dwFlags; DWORD dwInnerContentType; } CRYPT_ENCRYPT_MESSAGE_PARA, *PCRYPT_ENCRYPT_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "c683c515-3061-48e3-a64a-2798bd1245b0")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_ENCRYPT_MESSAGE_PARA
	{
		/// <summary>The size, in bytes, of this structure.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// The type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining
		/// them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgEncodingType;

		/// <summary>
		/// <para>This member is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> The handle to the cryptographic service provider (CSP) to be used for encryption.
		/// The CSP identified by <c>hCryptProv</c> is used to do content encryption, recipient key encryption, and recipient key
		/// export. Its private key is not used.
		/// </para>
		/// <para>
		/// Unless there is a strong reason for passing in a specific cryptographic provider in <c>hCryptProv</c>, pass zero to use the
		/// default RSA or DSS provider.
		/// </para>
		/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// <para>
		/// A CRYPT_ALGORITHM_IDENTIFIER structure that contains the object identifier (OID) of the encryption algorithm to use. The CSP
		/// specified by the <c>hCryptProv</c> must support this encryption algorithm.
		/// </para>
		/// <para>
		/// The <c>szOID_OIWSEC_desCBC</c> (CALG_DES) and <c>szOID_RSA_DES_EDE3_CBC</c> (CALG_3DES) encryption algorithms require the
		/// <c>Parameters</c> member of this structure to contain an encoded eight-byte initialization vector (IV). If the <c>cbData</c>
		/// member of the <c>Parameters</c> member is zero, an Abstract Syntax Notation One (ASN.1)-encoded OCTET STRING that contains
		/// the IV is generated using CryptGenRandom. For more information about the KP_IV parameter, see CryptSetKeyParam.
		/// </para>
		/// <para>
		/// The <c>szOID_NIST_AES128_CBC</c> (BCRYPT_AES_ALGORITHM, 128 bit), <c>szOID_NIST_AES192_CBC</c> (BCRYPT_AES_ALGORITHM, 192
		/// bit), and <c>szOID_NIST_AES256_CBC</c> (BCRYPT_AES_ALGORITHM, 256 bit) encryption algorithms require the <c>Parameters</c>
		/// member of this structure to contain an encoded sixteen-byte initialization vector (IV). If the <c>cbData</c> member of the
		/// Parameters member is zero, an Abstract Syntax Notation One (ASN.1)-encoded OCTET STRING that contains the IV is generated.
		/// </para>
		/// <para>
		/// The <c>szOID_RSA_RC2CBC</c> (CALG_RC2) algorithm requires the <c>pbData</c> member of the <c>Parameters</c> member of this
		/// structure to be a CRYPT_RC2_CBC_PARAMETERS structure. If the <c>cbData</c> member of the <c>Parameters</c> member is zero,
		/// an ASN.1-encoded <c>CRYPT_RC2_CBC_PARAMETERS</c> structure that contains the IV is generated as the <c>pbData</c> member.
		/// This generated <c>pbData</c> uses the default <c>dwVersion</c> that corresponds to the 40-bit key length. To override the
		/// default 40-bit key length, <c>pvEncryptionAuxInfo</c> can be set to point to a CMSG_RC2_AUX_INFO structure that contains a
		/// key bit length.
		/// </para>
		/// <para>
		/// <c>Note</c> When a message is decrypted, if it has an initialization vector parameter, the cryptographic message functions
		/// call CryptSetKeyParam with the initialization vector before decrypting.
		/// </para>
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER ContentEncryptionAlgorithm;

		/// <summary>
		/// <para>
		/// A pointer to a CMSG_RC2_AUX_INFO structure for RC2 encryption or a CMSG_SP3_COMPATIBLE_AUX_INFO structure for SP3-compatible
		/// encryption. For other than RC2 or SP3-compatible encryption, this member must be set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// If the <c>ContentEncryptionAlgorithm</c> member contains <c>szOID_RSA_RC4</c>, this member points to a CMSG_RC4_AUX_INFO
		/// structure that specifies the number of salt bytes to be included.
		/// </para>
		/// </summary>
		public IntPtr pvEncryptionAuxInfo;

		/// <summary>
		/// <para>
		/// Normally set to zero. However, if the encoded output is to be a CMSG_ENVELOPED inner content of an outer cryptographic
		/// message, such as a CMSG_SIGNED message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set. If it is not set, content will
		/// be encoded as an inner content type of CMSG_DATA.
		/// </para>
		/// <para>
		/// CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content within an OCTET STRING before encrypting.
		/// </para>
		/// <para>
		/// CRYPT_MESSAGE_KEYID_RECIPIENT_FLAG can be set to identify recipients by their Key Identifier and not their Issuer and Serial Number.
		/// </para>
		/// </summary>
		public CryptMsgActionFlags dwFlags;

		/// <summary>
		/// Normally set to zero. The <c>dwInnerContentType</c> member must be set to set the cryptographic message types if the input
		/// to be encrypted is the encoded output of another cryptographic message such as CMSG_SIGNED.
		/// </summary>
		public CryptMsgType dwInnerContentType;
	}

	/// <summary>The <c>CRYPT_HASH_MESSAGE_PARA</c> structure contains data for hashing messages.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_hash_message_para typedef struct
	// _CRYPT_HASH_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; CRYPT_ALGORITHM_IDENTIFIER
	// HashAlgorithm; void *pvHashAuxInfo; } CRYPT_HASH_MESSAGE_PARA, *PCRYPT_HASH_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "60415136-3ac0-4fab-bdbf-faa16e8e43e1")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_HASH_MESSAGE_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
		/// with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgEncodingType;

		/// <summary>
		/// <para>This member is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider (CSP) to be used.Unless there is a
		/// strong reason for passing in a specific cryptographic provider in <c>hCryptProv</c>, pass zero to use the default RSA or DSS provider.
		/// </para>
		/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>CRYPT_ALGORITHM_IDENTIFIER containing the algorithm for generating the hash of the message.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>Not currently used, and must be set to <c>NULL</c>.</summary>
		public IntPtr pvHashAuxInfo;
	}

	/// <summary>
	/// The <c>CRYPT_KEY_SIGN_MESSAGE_PARA</c> structure contains information about the cryptographic service provider (CSP) and
	/// algorithms used to sign a message.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_key_sign_message_para typedef struct
	// _CRYPT_KEY_SIGN_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE
	// hNCryptKey; } DUMMYUNIONNAME; DWORD dwKeySpec; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; void *pvHashAuxInfo;
	// CRYPT_ALGORITHM_IDENTIFIER PubKeyAlgorithm; } CRYPT_KEY_SIGN_MESSAGE_PARA, *PCRYPT_KEY_SIGN_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "d5426ad6-2181-42ce-99f2-cc6cc83e20a8")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_KEY_SIGN_MESSAGE_PARA
	{
		/// <summary>The size, in bytes, of this data structure.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Specifies the type of message and certificate encoding used. This can be a combination of one or more of the following values.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// <term>Specifies X.509 certificate encoding.</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// <term>Specifies PKCS 7 message encoding.</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgAndCertEncodingType;

		/// <summary>
			/// The handle of the CSP to use to sign the message. The CryptAcquireContext function is called to obtain this handle.
			/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
			/// The handle of the Cryptography API: Next Generation (CNG) CSP to use to sign the message. CNG signature algorithms are
			/// only supported in CNG functions.
			/// </summary>
		public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey { readonly get => (NCrypt.NCRYPT_KEY_HANDLE)(IntPtr)hCryptProv; set => hCryptProv = value.DangerousGetHandle(); }

		/// <summary>
		/// <para>
		/// Identifies the type of private key to use to sign the message. This must be one of the following values. This member is
		/// ignored if a CNG key is passed in the hNCryptKey member.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>AT_KEYEXCHANGE</term>
		/// <term>Use the key exchange key.</term>
		/// </item>
		/// <item>
		/// <term>AT_SIGNATURE</term>
		/// <term>Use the digital signature key.</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertKeySpec dwKeySpec;

		/// <summary>
		/// A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm to use to generate the hash of the message. This must be
		/// a hash algorithm.
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>This member is not used and must be set to <c>NULL</c>.</summary>
		public IntPtr pvHashAuxInfo;

		/// <summary>
		/// A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm to use to sign the message. This must be either a public
		/// key or a signature algorithm.
		/// </summary>
		public CRYPT_ALGORITHM_IDENTIFIER PubKeyAlgorithm;
	}

	/// <summary>
	/// The <c>CRYPT_KEY_VERIFY_MESSAGE_PARA</c> structure contains information needed to verify signed messages without a certificate
	/// for the signer.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_key_verify_message_para typedef struct
	// _CRYPT_KEY_VERIFY_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; }
	// CRYPT_KEY_VERIFY_MESSAGE_PARA, *PCRYPT_KEY_VERIFY_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "4e0178fb-1f9f-4ee4-9a83-f37cf71d35ff")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_KEY_VERIFY_MESSAGE_PARA
	{
		/// <summary>The size, in bytes, of this structure.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
		/// with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgEncodingType;

		/// <summary>
		/// <para>This member is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider (CSP) to be used to verify a
		/// signed message. The CSP identified by this handle is used for hashing and for signature verification.Unless there is a
		/// strong reason for using a specific cryptographic provider, set this member to zero to use the default RSA or DSS provider.
		/// </para>
		/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
		/// </summary>
		public HCRYPTPROV hCryptProv;
	}

	/// <summary>
	/// The <c>CRYPT_SIGN_MESSAGE_PARA</c> structure contains information for signing messages using a specified signing certificate context.
	/// </summary>
	/// <remarks>
	/// The <c>HashEncryptionAlgorithm</c> and <c>pvHashEncryptionAuxInfo</c> members can only be used if
	/// CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_sign_message_para typedef struct
	// _CRYPT_SIGN_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; PCCERT_CONTEXT pSigningCert; CRYPT_ALGORITHM_IDENTIFIER
	// HashAlgorithm; void *pvHashAuxInfo; DWORD cMsgCert; PCCERT_CONTEXT *rgpMsgCert; DWORD cMsgCrl; PCCRL_CONTEXT *rgpMsgCrl; DWORD
	// cAuthAttr; PCRYPT_ATTRIBUTE rgAuthAttr; DWORD cUnauthAttr; PCRYPT_ATTRIBUTE rgUnauthAttr; DWORD dwFlags; DWORD
	// dwInnerContentType; CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; void *pvHashEncryptionAuxInfo; } CRYPT_SIGN_MESSAGE_PARA, *PCRYPT_SIGN_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "1601d860-6054-4650-a033-ea088655b7e4")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_SIGN_MESSAGE_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
		/// with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgEncodingType;

		/// <summary>
		/// <para>A pointer to the CERT_CONTEXT to be used in the signing.</para>
		/// <para>
		/// Either the CERT_KEY_PROV_INFO_PROP_ID, or CERT_KEY_CONTEXT_PROP_ID property must be set for the context to provide access to
		/// the private signature key.
		/// </para>
		/// </summary>
		public PCCERT_CONTEXT pSigningCert;

		/// <summary>CRYPT_ALGORITHM_IDENTIFIER containing the hashing algorithm used to hash the data to be signed.</summary>
		public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

		/// <summary>Not currently used, and must be set to <c>NULL</c>.</summary>
		public IntPtr pvHashAuxInfo;

		/// <summary>
		/// Number of elements in the <c>rgpMsgCert</c> array of CERT_CONTEXT structures. If set to zero no certificates are included in
		/// the signed message.
		/// </summary>
		public uint cMsgCert;

		/// <summary>
		/// Array of pointers to CERT_CONTEXT structures to be included in the signed message. If the <c>pSigningCert</c> is to be
		/// included, a pointer to it must be in the <c>rgpMsgCert</c> array.
		/// </summary>
		public ArrayPointer<CERT_CONTEXT> rgpMsgCert;

		/// <summary>
		/// Number of elements in the <c>rgpMsgCrl</c> array of pointers to CRL_CONTEXT structures. If set to zero, no
		/// <c>CRL_CONTEXT</c> structures are included in the signed message.
		/// </summary>
		public uint cMsgCrl;

		/// <summary>Array of pointers to CRL_CONTEXT structures to be included in the signed message.</summary>
		public ArrayPointer<CRL_CONTEXT> rgpMsgCrl;

		/// <summary>
		/// Number of elements in the <c>rgAuthAttr</c> array. If no authenticated attributes are present in <c>rgAuthAttr</c>, this
		/// member is set to zero.
		/// </summary>
		public uint cAuthAttr;

		/// <summary>
		/// Array of pointers to CRYPT_ATTRIBUTE structures, each holding authenticated attribute information. If there are
		/// authenticated attributes present, the PKCS #9 standard dictates that there must be at least two attributes present, the
		/// content type object identifier (OID), and the hash of the message itself. These attributes are automatically added by the system.
		/// </summary>
		public ArrayPointer<CRYPT_ATTRIBUTE> rgAuthAttr;

		/// <summary>
		/// Number of elements in the <c>rgUnauthAttr</c> array. If no unauthenticated attributes are present in <c>rgUnauthAttr</c>,
		/// this member is zero.
		/// </summary>
		public uint cUnauthAttr;

		/// <summary>
		/// Array of pointers to CRYPT_ATTRIBUTE structures each holding an unauthenticated attribute information. Unauthenticated
		/// attributes can be used to contain countersignatures, among other uses.
		/// </summary>
		public ArrayPointer<CRYPT_ATTRIBUTE> rgUnauthAttr;

		/// <summary>
		/// <para>
		/// Normally zero. If the encoded output is to be a CMSG_SIGNED inner content of an outer cryptographic message such as a
		/// CMSG_ENVELOPED message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set. If it is not set, the message will be encoded
		/// as an inner content type of CMSG_DATA.
		/// </para>
		/// <para>
		/// CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content into an OCTET STRING.
		/// CRYPT_MESSAGE_KEYID_SIGNER_FLAG can be set to identify signers by their Key Identifier and not their Issuer and Serial Number.
		/// </para>
		/// <para>
		/// CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT flag,
		/// see CryptAcquireContext.
		/// </para>
		/// </summary>
		public CryptMsgActionFlags dwFlags;

		/// <summary>
		/// Normally zero. Set to the encoding type of the input message if that input to be signed is the encoded output of another
		/// cryptographic message.
		/// </summary>
		public uint dwInnerContentType;

		/// <summary>
		/// A CRYPT_ALGORITHM_IDENTIFIER. If present and not <c>NULL</c>, it is used instead of the signer's certificate
		/// <c>PublicKeyInfo.Algorithm</c> member. Note that for RSA, the hash encryption algorithm is normally the same as the public
		/// key algorithm. For DSA, the hash encryption algorithm is normally a DSS signature algorithm. This member can only be used if
		/// CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
		/// </summary>
		[MarshalAs(UnmanagedType.FunctionPtr)]
		public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

		/// <summary>
		/// Currently not used and must be set to <c>NULL</c>. This member can only be used if CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
		/// </summary>
		public IntPtr pvHashEncryptionAuxInfo;
	}

	/// <summary>The <c>CRYPT_VERIFY_MESSAGE_PARA</c> structure contains information needed to verify signed messages.</summary>
	/// <remarks>
	/// <para>This structure is passed to the following functions:</para>
	/// <list type="bullet">
	/// <item>
	/// <term>CryptDecodeMessage</term>
	/// </item>
	/// <item>
	/// <term>CryptDecryptAndVerifyMessageSignature</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifyDetachedMessageSignature</term>
	/// </item>
	/// <item>
	/// <term>CryptVerifyMessageSignature</term>
	/// </item>
	/// </list>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_verify_message_para typedef struct
	// _CRYPT_VERIFY_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; HCRYPTPROV_LEGACY hCryptProv;
	// PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate; void *pvGetArg; PCCERT_STRONG_SIGN_PARA pStrongSignPara; }
	// CRYPT_VERIFY_MESSAGE_PARA, *PCRYPT_VERIFY_MESSAGE_PARA;
	[PInvokeData("wincrypt.h", MSDNShortId = "bbd56b5e-2bbe-420f-8842-1be50dca779f")]
	[StructLayout(LayoutKind.Sequential)]
	public struct CRYPT_VERIFY_MESSAGE_PARA
	{
		/// <summary>Size of this structure in bytes.</summary>
		public uint cbSize;

		/// <summary>
		/// <para>
		/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
		/// with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </summary>
		public CertEncodingType dwMsgAndCertEncodingType;

		/// <summary>
		/// <para>This member is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider to be used to verify a signed
		/// message. The CSP identified by this handle is used for hashing and for signature verification.Unless there is a strong
		/// reason for using a specific cryptographic provider, set to zero to use the default RSA or DSS provider.
		/// </para>
		/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
		/// </summary>
		public HCRYPTPROV hCryptProv;

		/// <summary>
		/// <para>
		/// A pointer to the callback function used to get the signer's certificate context. If <c>NULL</c>, the default callback is
		/// used. The default callback tries to get the signer certificate context from the message's certificate store.
		/// </para>
		/// <para>
		/// An application defined–callback function that gets the signer's certificate can be used in place of the default. It is
		/// passed the certificate identifier of the signer (its issuer and serial number) and a handle to its cryptographic signed
		/// message's certificate store.
		/// </para>
		/// <para>See CryptGetSignerCertificateCallback for the callback functions signature and arguments.</para>
		/// </summary>
		public PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate;

		/// <summary>Argument to pass to the callback function. Typically, this gets and verifies the message signer's certificate.</summary>
		public IntPtr pvGetArg;

		/// <summary>
		/// <para>
		/// Optional pointer to a CERT_STRONG_SIGN_PARA structure that contains parameters used for strong signing. If you set this
		/// member and the function successfully verifies the signature, the function will then check for a strong signature. If the
		/// signature is not strong, the operation will fail and set the GetLastError value to <c>NTE_BAD_ALGID</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> You can use the <c>pStrongSignPara</c> member only if <c>CRYPT_VERIFY_MESSAGE_PARA_HAS_EXTRA_FIELDS</c> is
		/// defined by using the <c>#define</c> directive before including Wincrypt.h. If
		/// <c>CRYPT_VERIFY_MESSAGE_PARA_HAS_EXTRA_FIELDS</c> is defined, you must zero all unused fields.
		/// </para>
		/// <para>Windows 8 and Windows Server 2012:</para>
		/// <para>Support for this member begins.</para>
		/// </summary>
		public StructPointer<CERT_STRONG_SIGN_PARA> pStrongSignPara;
	}
}